root/src/modules/httptrap.c

Revision 70c30ef22f6d1312375e0392164d755db5824cb5, 26.2 kB (checked in by Phil Maddox <philip.maddox@circonus.com>, 2 days 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) 2011, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  * Copyright (c) 2011-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 #include <mtev_defines.h>
34
35 #include <stdio.h>
36 #include <unistd.h>
37 #include <errno.h>
38 #include <math.h>
39 #include <ctype.h>
40 #include <yajl/yajl_parse.h>
41
42 #include <mtev_rest.h>
43 #include <mtev_hash.h>
44
45 #include "noit_module.h"
46 #include "noit_check.h"
47 #include "noit_check_tools.h"
48 #include "noit_mtev_bridge.h"
49
50 #define DEFAULT_HTTPTRAP_DELIMITER '`'
51 #define MAX_DEPTH 32
52
53 #define HT_EX_TYPE 0x1
54 #define HT_EX_VALUE 0x2
55 #define HT_EX_TS 0x4
56 #define HT_EX_TAGS 0x8
57
58 static mtev_log_stream_t nlerr = NULL;
59 static mtev_log_stream_t nldeb = NULL;
60 static mtev_log_stream_t nlyajl = NULL;
61
62 #define _YD(fmt...) mtevL(nlyajl, fmt)
63
64 static mtev_boolean httptrap_surrogate;
65
66 typedef struct _mod_config {
67   mtev_hash_table *options;
68   mtev_boolean asynch_metrics;
69 } httptrap_mod_config_t;
70
71 typedef struct httptrap_closure_s {
72   noit_module_t *self;
73   int stats_count;
74 } httptrap_closure_t;
75
76 struct value_list {
77   char *v;
78   struct value_list *next;
79 };
80 struct rest_json_payload {
81   noit_check_t *check;
82   uuid_t check_id;
83   yajl_handle parser;
84   int len;
85   int complete;
86   char delimiter;
87   char *error;
88   int depth;
89   char *keys[MAX_DEPTH];
90   int array_depth[MAX_DEPTH];
91   unsigned char last_special_key;
92   unsigned char saw_complex_type;
93  
94   metric_type_t last_type;
95   struct value_list *last_value;
96   int cnt;
97   mtev_boolean immediate;
98 };
99
100 #define NEW_LV(json,a) do { \
101   struct value_list *nlv = malloc(sizeof(*nlv)); \
102   nlv->v = a; \
103   nlv->next = json->last_value; \
104   json->last_value = nlv; \
105 } while(0)
106
107 static mtev_boolean
108 noit_httptrap_check_asynch(noit_module_t *self,
109                            noit_check_t *check) {
110   const char *config_val;
111   httptrap_mod_config_t *conf;
112   if(!self) return mtev_true;
113   conf = noit_module_get_userdata(self);
114   if(!conf) return mtev_true;
115   mtev_boolean is_asynch = conf->asynch_metrics;
116   if(mtev_hash_retr_str(check->config,
117                         "asynch_metrics", strlen("asynch_metrics"),
118                         (const char **)&config_val)) {
119     if(!strcasecmp(config_val, "false") || !strcasecmp(config_val, "off"))
120       is_asynch = mtev_false;
121     else if(!strcasecmp(config_val, "true") || !strcasecmp(config_val, "on"))
122       is_asynch = mtev_true;
123   }
124
125   if(is_asynch) check->flags |= NP_SUPPRESS_METRICS;
126   else check->flags &= ~NP_SUPPRESS_METRICS;
127   return is_asynch;
128 }
129
130 static int
131 set_array_key(struct rest_json_payload *json) {
132   if(json->array_depth[json->depth] > 0) {
133     char str[256];
134     int strLen;
135     snprintf(str, sizeof(str), "%d", json->array_depth[json->depth] - 1);
136     json->array_depth[json->depth]++;
137     strLen = strlen(str);
138     if(json->keys[json->depth]) free(json->keys[json->depth]);
139     json->keys[json->depth] = NULL;
140     if(json->depth == 0) {
141       json->keys[json->depth] = malloc(strLen+1);
142       memcpy(json->keys[json->depth], str, strLen);
143       json->keys[json->depth][strLen] = '\0';
144     }
145     else {
146       int uplen = strlen(json->keys[json->depth-1]);
147       /* This is too large.... return an error */
148       if(uplen + 1 + strLen > 255) {
149         return -1;
150       }
151       json->keys[json->depth] = malloc(uplen + 1 + strLen + 1);
152       memcpy(json->keys[json->depth], json->keys[json->depth-1], uplen);
153       json->keys[json->depth][uplen] = json->delimiter;
154       memcpy(json->keys[json->depth] + uplen + 1, str, strLen);
155       json->keys[json->depth][uplen + 1 + strLen] = '\0';
156     }
157   }
158   return 0;
159 }
160 static int
161 httptrap_yajl_cb_null(void *ctx) {
162   struct rest_json_payload *json = ctx;
163   int rv;
164   if(json->depth<0) {
165     _YD("[%3d] cb_null [BAD]\n", json->depth);
166     return 0;
167   }
168   rv = set_array_key(json);
169   if(json->last_special_key == HT_EX_VALUE) {
170     _YD("[%3d]*cb_null\n", json->depth);
171     NEW_LV(json, NULL);
172     return 1;
173   }
174   if(json->last_special_key) return 0;
175   if(rv) return 1;
176   if(json->keys[json->depth]) {
177     _YD("[%3d] cb_null\n", json->depth);
178     noit_stats_set_metric(json->check,
179         json->keys[json->depth], METRIC_INT32, NULL);
180     if(json->immediate)
181       noit_stats_log_immediate_metric(json->check,
182           json->keys[json->depth], METRIC_INT32, NULL);
183     json->cnt++;
184   }
185   return 1;
186 }
187 static int
188 httptrap_yajl_cb_boolean(void *ctx, int boolVal) {
189   int ival, rv;
190   struct rest_json_payload *json = ctx;
191   if(json->depth<0) {
192     _YD("[%3d] cb_boolean [BAD]\n", json->depth);
193     return 0;
194   }
195   rv = set_array_key(json);
196   if(json->last_special_key == HT_EX_VALUE) {
197     NEW_LV(json, strdup(boolVal ? "1" : "0"));
198     _YD("[%3d]*cb_boolean -> %s\n", json->depth, boolVal ? "true" : "false");
199     return 1;
200   }
201   if(json->last_special_key) return 0;
202   if(rv) return 1;
203   if(json->keys[json->depth]) {
204     ival = boolVal ? 1 : 0;
205     _YD("[%3d] cb_boolean -> %s\n", json->depth, boolVal ? "true" : "false");
206     noit_stats_set_metric(json->check,
207         json->keys[json->depth], METRIC_INT32, &ival);
208     if(json->immediate)
209       noit_stats_log_immediate_metric(json->check,
210           json->keys[json->depth], METRIC_INT32, &ival);
211     json->cnt++;
212   }
213   return 1;
214 }
215 static int
216 httptrap_yajl_cb_number(void *ctx, const char * numberVal,
217                         size_t numberLen) {
218   char val[128];
219   struct rest_json_payload *json = ctx;
220   int rv;
221   if(json->depth<0) {
222     _YD("[%3d] cb_number [BAD]\n", json->depth);
223     return 0;
224   }
225   rv = set_array_key(json);
226   if(json->last_special_key == HT_EX_VALUE) {
227     char *str;
228     str = malloc(numberLen+1);
229     memcpy(str, numberVal, numberLen);
230     str[numberLen] = '\0';
231     NEW_LV(json, str);
232     _YD("[%3d] cb_number %s\n", json->depth, str);
233     return 1;
234   }
235   if(rv) return 1;
236   if(json->last_special_key == HT_EX_TS) return 1;
237   if(json->last_special_key) {
238     _YD("[%3d] cb_number [BAD]\n", json->depth);
239     return 0;
240   }
241   if(json->keys[json->depth]) {
242     if(numberLen > sizeof(val)-1) numberLen = sizeof(val)-1;
243     memcpy(val, numberVal, numberLen);
244     val[numberLen] = '\0';
245     _YD("[%3d] cb_number %s\n", json->depth, val);
246     noit_stats_set_metric(json->check,
247         json->keys[json->depth], METRIC_GUESS, val);
248     if(json->immediate)
249       noit_stats_log_immediate_metric(json->check,
250           json->keys[json->depth], METRIC_GUESS, val);
251     json->cnt++;
252   }
253   return 1;
254 }
255 static int
256 httptrap_yajl_cb_string(void *ctx, const unsigned char * stringVal,
257                         size_t stringLen) {
258   struct rest_json_payload *json = ctx;
259   char val[4096];
260   int rv;
261   if(json->depth<0) {
262     _YD("[%3d] cb_string [BAD]\n", json->depth);
263     return 0;
264   }
265   if(json->last_special_key == HT_EX_TS) /* handle ts */
266     return 1;
267   if(json->last_special_key == HT_EX_TAGS) /* handle tag */
268     return 1;
269   rv = set_array_key(json);
270   if(json->last_special_key == HT_EX_TYPE) {
271     if(stringLen != 1) return 0;
272     if(*stringVal == 'L' || *stringVal == 'l' ||
273         *stringVal == 'I' || *stringVal == 'i' ||
274         *stringVal == 'n' || *stringVal == 's') {
275       json->last_type = *stringVal;
276       json->saw_complex_type |= HT_EX_TYPE;
277       _YD("[%3d] cb_string { _type: %c }\n", json->depth, *stringVal);
278       return 1;
279     }
280     _YD("[%3d] cb_string { bad _type: %.*s }\n", json->depth,
281         (int)stringLen, stringVal);
282     return 0;
283   }
284   else if(json->last_special_key == HT_EX_VALUE) {
285     char *str;
286     str = malloc(stringLen+1);
287     memcpy(str, stringVal, stringLen);
288     str[stringLen] = '\0';
289     NEW_LV(json, str);
290     _YD("[%3d] cb_string { _value: %s }\n", json->depth, str);
291     json->saw_complex_type |= HT_EX_VALUE;
292     return 1;
293   }
294   else if(json->last_special_key == HT_EX_TS) return 1;
295   else if(json->last_special_key == HT_EX_TAGS) return 1;
296   if(rv) return 1;
297   if(json->keys[json->depth]) {
298     if(stringLen > sizeof(val)-1) stringLen = sizeof(val)-1;
299     memcpy(val, stringVal, stringLen);
300     val[stringLen] = '\0';
301     _YD("[%3d] cb_string %s\n", json->depth, val);
302     noit_stats_set_metric(json->check,
303         json->keys[json->depth], METRIC_GUESS, val);
304     if(json->immediate)
305       noit_stats_log_immediate_metric(json->check,
306           json->keys[json->depth], METRIC_GUESS, val);
307     json->cnt++;
308   }
309   return 1;
310 }
311 static int
312 httptrap_yajl_cb_start_map(void *ctx) {
313   struct rest_json_payload *json = ctx;
314   _YD("[%3d] cb_start_map\n", json->depth);
315   if(set_array_key(json)) return 1;
316   json->depth++;
317   if(json->depth >= MAX_DEPTH) return 0;
318   return 1;
319 }
320 static int
321 httptrap_yajl_cb_end_map(void *ctx) {
322   struct value_list *p, *last_p = NULL;
323   struct rest_json_payload *json = ctx;
324   _YD("[%3d]%-.*s cb_end_map\n", json->depth, json->depth, "");
325   json->depth--;
326   if(json->saw_complex_type == 0x3) {
327     long double total = 0, cnt = 0;
328     mtev_boolean use_avg = mtev_false;
329     for(p=json->last_value;p;p=p->next) {
330       noit_stats_set_metric_coerce(json->check,
331           json->keys[json->depth], json->last_type, p->v);
332       last_p = p;
333       if(p->v != NULL &&
334          (json->last_type == 'L' || json->last_type == 'l' ||
335           json->last_type == 'I' || json->last_type == 'i' ||
336           json->last_type == 'n')) {
337         total += strtold(p->v, NULL);
338         cnt = cnt + 1;
339         use_avg = mtev_true;
340       }
341       json->cnt++;
342     }
343     if(json->immediate && last_p != NULL) {
344       if(use_avg) {
345         double avg = total / cnt;
346         noit_stats_log_immediate_metric(json->check,
347             json->keys[json->depth], 'n', &avg);
348       }
349       else {
350         noit_stats_log_immediate_metric(json->check,
351             json->keys[json->depth], json->last_type, last_p->v);
352       }
353     }
354   }
355   json->saw_complex_type = 0;
356   for(p=json->last_value;p;) {
357     struct value_list *savenext;
358     savenext = p->next;
359     if(p->v) free(p->v);
360     savenext = p->next;
361     free(p);
362     p = savenext;
363   }
364   json->last_value = NULL;
365   return 1;
366 }
367 static int
368 httptrap_yajl_cb_start_array(void *ctx) {
369   struct rest_json_payload *json = ctx;
370   set_array_key(json);
371   json->depth++;
372   json->array_depth[json->depth]++;
373   return 1;
374 }
375 static int
376 httptrap_yajl_cb_end_array(void *ctx) {
377   struct rest_json_payload *json = ctx;
378   json->array_depth[json->depth] = 0;
379   json->depth--;
380   return 1;
381 }
382 static int
383 httptrap_yajl_cb_map_key(void *ctx, const unsigned char * key,
384                          size_t stringLen) {
385   struct rest_json_payload *json = ctx;
386   if(stringLen > 255) return 0;
387   if(json->keys[json->depth]) free(json->keys[json->depth]);
388   json->keys[json->depth] = NULL;
389   if(stringLen == 5 && memcmp(key, "_type", 5) == 0) {
390     json->last_special_key = HT_EX_TYPE;
391     if(json->depth > 0) json->keys[json->depth] = strdup(json->keys[json->depth-1]);
392     return 1;
393   }
394   if(stringLen == 6 && memcmp(key, "_value", 6) == 0) {
395     if(json->depth > 0) json->keys[json->depth] = strdup(json->keys[json->depth-1]);
396     json->last_special_key = HT_EX_VALUE;
397     json->saw_complex_type |= HT_EX_VALUE;
398     return 1;
399   }
400   if(stringLen == 3 && memcmp(key, "_ts", 3) == 0) {
401     json->last_special_key = HT_EX_TS;
402     return 1;
403   }
404   if(stringLen == 5 && memcmp(key, "_tags", 5) == 0) {
405     json->last_special_key = HT_EX_TAGS;
406     return 1;
407   }
408   json->last_special_key = 0;
409   if(json->depth == 0) {
410     json->keys[json->depth] = malloc(stringLen+1);
411     memcpy(json->keys[json->depth], key, stringLen);
412     json->keys[json->depth][stringLen] = '\0';
413   }
414   else {
415     int uplen = strlen(json->keys[json->depth-1]);
416     if(uplen + 1 + stringLen > 255) return 0;
417     json->keys[json->depth] = malloc(uplen + 1 + stringLen + 1);
418     memcpy(json->keys[json->depth], json->keys[json->depth-1], uplen);
419     json->keys[json->depth][uplen] = json->delimiter;
420     memcpy(json->keys[json->depth] + uplen + 1, key, stringLen);
421     json->keys[json->depth][uplen + 1 + stringLen] = '\0';
422   }
423   return 1;
424 }
425 static yajl_callbacks httptrap_yajl_callbacks = {
426   .yajl_null = httptrap_yajl_cb_null,
427   .yajl_boolean = httptrap_yajl_cb_boolean,
428   .yajl_number = httptrap_yajl_cb_number,
429   .yajl_string = httptrap_yajl_cb_string,
430   .yajl_start_map = httptrap_yajl_cb_start_map,
431   .yajl_map_key = httptrap_yajl_cb_map_key,
432   .yajl_end_map = httptrap_yajl_cb_end_map,
433   .yajl_start_array = httptrap_yajl_cb_start_array,
434   .yajl_end_array = httptrap_yajl_cb_end_array
435 };
436
437 static void
438 rest_json_payload_free(void *f) {
439   int i;
440   struct rest_json_payload *json = f;
441   if(json->parser) yajl_free(json->parser);
442   if(json->error) free(json->error);
443   for(i=0;i<MAX_DEPTH;i++)
444     if(json->keys[i]) free(json->keys[i]);
445   if(json->last_value) free(json->last_value);
446   free(json);
447 }
448
449 static struct rest_json_payload *
450 rest_get_json_upload(mtev_http_rest_closure_t *restc,
451                     int *mask, int *complete) {
452   struct rest_json_payload *rxc;
453   mtev_http_request *req = mtev_http_session_request(restc->http_ctx);
454   httptrap_closure_t *ccl = NULL;
455   int content_length;
456   char buffer[32768];
457
458   content_length = mtev_http_request_content_length(req);
459   rxc = restc->call_closure;
460   rxc->check = noit_poller_lookup(rxc->check_id);
461   if (!rxc->check) {
462     *complete = 1;
463     return NULL;
464   }
465
466   if(!strcmp(rxc->check->module, "httptrap")) ccl = rxc->check->closure;
467   rxc->immediate = noit_httptrap_check_asynch(ccl ? ccl->self : NULL, rxc->check);
468   while(!rxc->complete) {
469     int len;
470     len = mtev_http_session_req_consume(
471             restc->http_ctx, buffer,
472             MIN(content_length - rxc->len, sizeof(buffer)),
473             sizeof(buffer),
474             mask);
475     if(len > 0) {
476       yajl_status status;
477       _YD("inbound payload chunk (%d bytes) continuing YAJL parse\n", len);
478       status = yajl_parse(rxc->parser, (unsigned char *)buffer, len);
479       if(status != yajl_status_ok) {
480         unsigned char *err;
481         *complete = 1;
482         err = yajl_get_error(rxc->parser, 0, (unsigned char *)buffer, len);
483         rxc->error = strdup((char *)err);
484         yajl_free_error(rxc->parser, err);
485         return rxc;
486       }
487       rxc->len += len;
488     }
489     if(len < 0 && errno == EAGAIN) return NULL;
490     else if(len < 0) {
491       *complete = 1;
492       return NULL;
493     }
494     content_length = mtev_http_request_content_length(req);
495     if((mtev_http_request_payload_chunked(req) && len == 0) ||
496        (rxc->len == content_length)) {
497       rxc->complete = 1;
498       _YD("no more data, finishing YAJL parse\n");
499       yajl_complete_parse(rxc->parser);
500     }
501   }
502
503   *complete = 1;
504   return rxc;
505 }
506
507 static int httptrap_submit(noit_module_t *self, noit_check_t *check,
508                            noit_check_t *cause) {
509   httptrap_closure_t *ccl;
510   struct timeval duration;
511   /* We are passive, so we don't do anything for transient checks */
512   if(check->flags & NP_TRANSIENT) return 0;
513
514   noit_httptrap_check_asynch(self, check);
515   if(!check->closure) {
516     ccl = check->closure = (void *)calloc(1, sizeof(httptrap_closure_t));
517     memset(ccl, 0, sizeof(httptrap_closure_t));
518     ccl->self = self;
519   } else {
520     // Don't count the first run
521     struct timeval now, *last;
522     char human_buffer[256];
523     ccl = (httptrap_closure_t*)check->closure;
524     gettimeofday(&now, NULL);
525     sub_timeval(now, check->last_fire_time, &duration);
526     noit_stats_set_whence(check, &now);
527     noit_stats_set_duration(check, duration.tv_sec * 1000 + duration.tv_usec / 1000);
528
529     snprintf(human_buffer, sizeof(human_buffer),
530              "dur=%ld,run=%d,stats=%d", duration.tv_sec * 1000 + duration.tv_usec / 1000,
531              check->generation, ccl->stats_count);
532     mtevL(nldeb, "httptrap(%s) [%s]\n", check->target, human_buffer);
533
534     // Not sure what to do here
535     noit_stats_set_available(check, (ccl->stats_count > 0) ?
536         NP_AVAILABLE : NP_UNAVAILABLE);
537     noit_stats_set_state(check, (ccl->stats_count > 0) ?
538         NP_GOOD : NP_BAD);
539     noit_stats_set_status(check, human_buffer);
540     if(check->last_fire_time.tv_sec)
541       noit_check_passive_set_stats(check);
542
543     memcpy(&check->last_fire_time, &now, sizeof(now));
544   }
545   ccl->stats_count = 0;
546   return 0;
547 }
548
549 static mtev_boolean
550 cross_module_reverse_allowed(noit_check_t *check, const char *secret) {
551   void *vstr;
552   mtev_hash_table *config;
553   static int reverse_check_module_id = -1;
554   if(reverse_check_module_id < 0) {
555     reverse_check_module_id = noit_check_registered_module_by_name("reverse");
556     if(reverse_check_module_id < 0) return mtev_false;
557   }
558   config = noit_check_get_module_config(check, reverse_check_module_id);
559   if(!config) return mtev_false;
560   if(mtev_hash_retrieve(config, "key", strlen("key"), &vstr)) {
561     if(!strcmp((const char *)vstr, secret)) return mtev_true;
562   }
563   return mtev_false;
564 }
565
566 static int
567 rest_httptrap_handler(mtev_http_rest_closure_t *restc,
568                       int npats, char **pats) {
569   int mask, complete = 0, cnt;
570   struct rest_json_payload *rxc = NULL;
571   const char *error = "internal error", *secret = NULL;
572   mtev_http_session_ctx *ctx = restc->http_ctx;
573   const unsigned int DEBUGDATA_OUT_SIZE=4096;
574   const unsigned int JSON_OUT_SIZE=DEBUGDATA_OUT_SIZE+128;
575   char json_out[JSON_OUT_SIZE];
576   char debugdata_out[DEBUGDATA_OUT_SIZE];
577   int debugflag=0;
578   const char *debugchkflag;
579   noit_check_t *check;
580   uuid_t check_id;
581   mtev_http_request *req;
582   mtev_hash_table *hdrs;
583
584   if(npats != 2) {
585     error = "bad uri";
586     goto error;
587   }
588   if(uuid_parse(pats[0], check_id)) {
589     error = "uuid parse error";
590     goto error;
591   }
592
593   if(restc->call_closure == NULL) {
594     mtev_boolean allowed = mtev_false;
595     httptrap_closure_t *ccl = NULL;
596     const char *delimiter = NULL;
597     rxc = restc->call_closure = calloc(1, sizeof(*rxc));
598     rxc->delimiter = DEFAULT_HTTPTRAP_DELIMITER;
599     check = noit_poller_lookup(check_id);
600     if(!check) {
601       error = "no such check";
602       goto error;
603     }
604     if(!httptrap_surrogate && strcmp(check->module, "httptrap")) {
605       error = "no such httptrap check";
606       goto error;
607     }
608  
609     /* check "secret" then "httptrap_secret" as a fallback */
610     (void)mtev_hash_retr_str(check->config, "secret", strlen("secret"), &secret);
611     if(!secret) (void)mtev_hash_retr_str(check->config, "httptrap_secret", strlen("httptrap_secret"), &secret);
612     if(secret && !strcmp(pats[1], secret)) allowed = mtev_true;
613     if(!allowed && cross_module_reverse_allowed(check, pats[1])) allowed = mtev_true;
614
615     if(!allowed) {
616       error = "secret mismatch";
617       goto error;
618     }
619
620     /* check "delimiter" then "httptrap_delimiter" as a fallback */
621     (void)mtev_hash_retr_str(check->config, "delimiter", strlen("delimiter"), &delimiter);
622     if(!delimiter) (void)mtev_hash_retr_str(check->config, "httptrap_delimiter", strlen("httptrap_delimiter"), &delimiter);
623     if(delimiter && *delimiter) rxc->delimiter = *delimiter;
624     rxc->check = check;
625     uuid_copy(rxc->check_id, check_id);
626     rxc->parser = yajl_alloc(&httptrap_yajl_callbacks, NULL, rxc);
627     rxc->depth = -1;
628     yajl_config(rxc->parser, yajl_allow_comments, 1);
629     yajl_config(rxc->parser, yajl_dont_validate_strings, 1);
630     yajl_config(rxc->parser, yajl_allow_trailing_garbage, 1);
631     yajl_config(rxc->parser, yajl_allow_partial_values, 1);
632     restc->call_closure_free = rest_json_payload_free;
633   }
634   else rxc = restc->call_closure;
635
636   /* flip threads */
637   {
638     mtev_http_connection *conn = mtev_http_session_connection(ctx);
639     eventer_t e = mtev_http_connection_event(conn);
640     if(e) {
641       pthread_t tgt = CHOOSE_EVENTER_THREAD_FOR_CHECK(rxc->check);
642       if(!pthread_equal(e->thr_owner, tgt)) {
643         e->thr_owner = tgt;
644         return EVENTER_READ | EVENTER_WRITE | EVENTER_EXCEPTION;
645       }
646     }
647   }
648
649   rxc = rest_get_json_upload(restc, &mask, &complete);
650   if(rxc == NULL && !complete) return mask;
651
652   if(!rxc) goto error;
653   if(rxc->error) goto error;
654
655   cnt = rxc->cnt;
656
657   mtev_http_response_status_set(ctx, 200, "OK");
658   mtev_http_response_header_set(ctx, "Content-Type", "application/json");
659   mtev_http_response_option_set(ctx, MTEV_HTTP_CLOSE);
660  
661   /*Examine headers for x-circonus-httptrap-debug flag*/
662   req = mtev_http_session_request(ctx);
663   hdrs = mtev_http_request_headers_table(req);
664    
665   /*Check if debug header passed in. If present and set to true, set debugflag value to one.*/
666   if(mtev_hash_retr_str(hdrs, "x-circonus-httptrap-debug", strlen("x-circonus-httptrap-debug"), &debugchkflag))
667   {
668     if (strcmp(debugchkflag,"true")==0)
669     {
670       debugflag=1;
671     }
672   }
673    
674   /*If debugflag remains zero, simply output the number of metrics.*/
675   if (debugflag==0)
676   {
677     snprintf(json_out, sizeof(json_out),
678            "{ \"stats\": %d }", cnt);   
679   }
680  
681   /*Otherwise, if set to one, output current metrics in addition to number of current metrics.*/
682   else if (debugflag==1)
683   {       
684       stats_t *c;
685       mtev_hash_table *metrics;
686        
687       /*Retrieve check information.*/       
688       check = noit_poller_lookup(check_id);
689       c = noit_check_get_stats_current(check);
690       metrics = noit_check_stats_metrics(c);
691       mtev_hash_iter iter = MTEV_HASH_ITER_ZERO;
692       const char *k;
693       int klen;
694       void *data;
695       int written=0;
696       int offset=0;
697       memset(debugdata_out,'\0',sizeof(debugdata_out));
698      
699       /*Extract metrics*/
700       while(mtev_hash_next(metrics, &iter, &k, &klen, &data))
701       {
702         char buff[256];
703         int toWrite = DEBUGDATA_OUT_SIZE-offset;
704         metric_t *tmp=(metric_t *)data;
705         char *metric_name=tmp->metric_name;
706         metric_type_t metric_type=tmp->metric_type;
707         noit_stats_snprint_metric_value(buff, sizeof(buff), tmp);
708         written = snprintf(debugdata_out + offset, toWrite, "\"%s\": {\"_type\":\"%c\",\"_value\":\"%s\"},", metric_name,metric_type,buff);
709         if(toWrite < written)
710         {
711             break;
712         }
713         offset += written;
714       }
715        
716       /*Set last character to empty-don't want extra comma in output*/
717       if (offset>1)
718       {
719         snprintf(debugdata_out + (offset-1), 1, "%s"," ");
720       }
721      
722       /*Output stats and metrics.*/
723       snprintf(json_out, sizeof(json_out)+strlen(debugdata_out),
724              "{ \"stats\": %d, \"metrics\": {%s } }", cnt, debugdata_out);
725   }
726
727   mtev_http_response_append(ctx, json_out, strlen(json_out));
728   mtev_http_response_end(ctx);
729   return 0;
730
731  error:
732   mtev_http_response_server_error(ctx, "application/json");
733   mtev_http_response_append(ctx, "{ \"error\": \"", 12);
734   if(rxc && rxc->error) error = rxc->error;
735   mtev_http_response_append(ctx, error, strlen(error));
736   mtev_http_response_append(ctx, "\" }", 3);
737   mtev_http_response_end(ctx);
738   return 0;
739 }
740
741 static int noit_httptrap_initiate_check(noit_module_t *self,
742                                         noit_check_t *check,
743                                         int once, noit_check_t *cause) {
744   check->flags |= NP_PASSIVE_COLLECTION;
745   if (check->closure == NULL) {
746     httptrap_closure_t *ccl;
747     ccl = check->closure = (void *)calloc(1, sizeof(httptrap_closure_t));
748     ccl->self = self;
749   }
750   INITIATE_CHECK(httptrap_submit, self, check, cause);
751   return 0;
752 }
753
754 static int noit_httptrap_config(noit_module_t *self, mtev_hash_table *options) {
755   httptrap_mod_config_t *conf;
756   conf = noit_module_get_userdata(self);
757   if(conf) {
758     if(conf->options) {
759       mtev_hash_destroy(conf->options, free, free);
760       free(conf->options);
761     }
762   }
763   else
764     conf = calloc(1, sizeof(*conf));
765   conf->options = options;
766   noit_module_set_userdata(self, conf);
767   return 1;
768 }
769
770 static int noit_httptrap_onload(mtev_image_t *self) {
771   if(!nlerr) nlerr = mtev_log_stream_find("error/httptrap");
772   if(!nldeb) nldeb = mtev_log_stream_find("debug/httptrap");
773   if(!nlyajl) nlyajl = mtev_log_stream_find("debug/httptrap_yajl");
774   if(!nlerr) nlerr = noit_error;
775   if(!nldeb) nldeb = noit_debug;
776   return 0;
777 }
778
779 static int noit_httptrap_init(noit_module_t *self) {
780   const char *config_val;
781   httptrap_mod_config_t *conf;
782   conf = noit_module_get_userdata(self);
783
784   conf->asynch_metrics = mtev_true;
785   if(mtev_hash_retr_str(conf->options,
786                         "asynch_metrics", strlen("asynch_metrics"),
787                         (const char **)&config_val)) {
788     if(!strcasecmp(config_val, "false") || !strcasecmp(config_val, "off"))
789       conf->asynch_metrics = mtev_false;
790   }
791
792   httptrap_surrogate = mtev_false;
793   if(mtev_hash_retr_str(conf->options,
794                         "surrogate", strlen("surrogate"),
795                         (const char **)&config_val)) {
796     if(!strcasecmp(config_val, "true") || !strcasecmp(config_val, "on"))
797       httptrap_surrogate = mtev_true;
798   }
799
800   noit_module_set_userdata(self, conf);
801
802   /* register rest handler */
803   mtev_http_rest_register("PUT", "/module/httptrap/",
804                           "^(" UUID_REGEX ")/([^/]*).*$",
805                           rest_httptrap_handler);
806   mtev_http_rest_register("POST", "/module/httptrap/",
807                           "^(" UUID_REGEX ")/([^/]*).*$",
808                           rest_httptrap_handler);
809   return 0;
810 }
811
812 #include "httptrap.xmlh"
813 noit_module_t httptrap = {
814   {
815     .magic = NOIT_MODULE_MAGIC,
816     .version = NOIT_MODULE_ABI_VERSION,
817     .name = "httptrap",
818     .description = "httptrap collection",
819     .xml_description = httptrap_xml_description,
820     .onload = noit_httptrap_onload
821   },
822   noit_httptrap_config,
823   noit_httptrap_init,
824   noit_httptrap_initiate_check,
825   NULL
826 };
Note: See TracBrowser for help on using the browser.