root/src/modules/http.c

Revision 4c05448ea043aaf96b94270f68092553c96ccd9d, 26.2 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 6 years ago)

little bits of valgrind ecstasy

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2007, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  */
5
6 #include "noit_defines.h"
7
8 #include <stdio.h>
9 #include <unistd.h>
10 #include <errno.h>
11 #include <assert.h>
12 #include <math.h>
13
14 #include <libxml/parser.h>
15 #include <libxml/tree.h>
16 #include <libxml/xpath.h>
17
18 #include "noit_module.h"
19 #include "noit_check.h"
20 #include "noit_check_tools.h"
21 #include "utils/noit_log.h"
22 #include "utils/noit_hash.h"
23
24 #include <apr_uri.h>
25 #include <apr_atomic.h>
26 #include <apr_strings.h>
27 #include "serf.h"
28
29 #define NOIT_HTTP_VERSION_STRING "0.1"
30
31 typedef struct {
32   noit_hash_table *options;
33   void (*results)(noit_module_t *, noit_check_t *);
34 } serf_module_conf_t;
35
36 typedef struct {
37   int using_ssl;
38   serf_ssl_context_t *ssl_ctx;
39   serf_bucket_alloc_t *bkt_alloc;
40 } app_baton_t;
41
42 typedef struct {
43   serf_response_acceptor_t acceptor;
44   app_baton_t *acceptor_baton;
45
46   serf_response_handler_t handler;
47   const char *host;
48   const char *method;
49   const char *path;
50   const char *authn;
51
52   noit_module_t *self;
53   noit_check_t *check;
54 } handler_baton_t;
55
56 typedef struct buf_t {
57   char *b;
58   int32_t l;
59 } buf_t;
60
61 typedef struct {
62   apr_pool_t *pool;
63   apr_sockaddr_t *address;
64   serf_context_t *context;
65   serf_connection_t *connection;
66   serf_request_t *request;
67   app_baton_t app_ctx;
68   handler_baton_t handler_ctx;
69   apr_uri_t url;
70   int timed_out;
71
72   serf_status_line status;
73   buf_t headers;
74   buf_t body;
75
76   struct timeval finish_time;
77   eventer_t fd_event;
78   eventer_t timeout_event;
79 } serf_check_info_t;
80
81 typedef struct {
82   serf_check_info_t serf;
83   struct timeval xml_doc_time;
84   char *xpathexpr;
85   xmlDocPtr xml_doc;
86   char *resmod;
87   char *resserv;
88 } resmon_check_info_t;
89
90 typedef struct {
91   noit_module_t *self;
92   noit_check_t *check;
93   void *serf_baton;
94   apr_socket_t *skt;
95 } serf_closure_t;
96
97 static noit_log_stream_t nlerr = NULL;
98 static noit_log_stream_t nldeb = NULL;
99 static int serf_handler(eventer_t e, int mask, void *closure,
100                         struct timeval *now);
101 static void serf_log_results(noit_module_t *self, noit_check_t *check);
102 static void resmon_log_results(noit_module_t *self, noit_check_t *check);
103 static void resmon_part_log_results(noit_module_t *self, noit_check_t *check,
104                                     noit_check_t *parent);
105
106 static int serf_config(noit_module_t *self, noit_hash_table *options) {
107   serf_module_conf_t *conf;
108   conf = noit_module_get_userdata(self);
109   if(conf) {
110     if(conf->options) {
111       noit_hash_destroy(conf->options, free, free);
112       free(conf->options);
113     }
114   }
115   else
116     conf = calloc(1, sizeof(*conf));
117   conf->options = options;
118   conf->results = serf_log_results;
119   noit_module_set_userdata(self, conf);
120   return 0;
121 }
122 static int resmon_config(noit_module_t *self, noit_hash_table *options) {
123   serf_module_conf_t *conf;
124   conf = noit_module_get_userdata(self);
125   if(conf) {
126     if(conf->options) {
127       noit_hash_destroy(conf->options, free, free);
128       free(conf->options);
129     }
130   }
131   else
132     conf = calloc(1, sizeof(*conf));
133   conf->options = options;
134   if(!conf->options) conf->options = calloc(1, sizeof(*conf->options));
135   noit_hash_store(conf->options, strdup("url"), strlen("url"),
136                   strdup("http://localhost:81/"));
137   conf->results = resmon_log_results;
138   noit_module_set_userdata(self, conf);
139   return 0;
140 }
141 static void generic_log_results(noit_module_t *self, noit_check_t *check) {
142   serf_module_conf_t *module_conf;
143   module_conf = noit_module_get_userdata(self);
144   module_conf->results(self, check);
145 }
146 static void serf_log_results(noit_module_t *self, noit_check_t *check) {
147   serf_check_info_t *ci = check->closure;
148   struct timeval duration;
149   stats_t current;
150   int expect_code = 200;
151   char *code_str;
152   char human_buffer[256], code[4], rt[14];
153
154   noit_check_stats_clear(&current);
155
156   if(noit_hash_retrieve(check->config, "code", strlen("code"),
157                         (void **)&code_str))
158     expect_code = atoi(code_str);
159
160   sub_timeval(ci->finish_time, check->last_fire_time, &duration);
161
162   snprintf(code, sizeof(code), "%3d", ci->status.code);
163   snprintf(rt, sizeof(rt), "%.3fs",
164            (float)duration.tv_sec + (float)duration.tv_usec / 1000000.0);
165   snprintf(human_buffer, sizeof(human_buffer),
166            "code=%s,rt=%s,bytes=%d",
167            ci->status.code ? code : "undefined",
168            ci->timed_out ? "timeout" : rt,
169            ci->body.l);
170   noitL(nldeb, "http(%s) [%s]\n", check->target, human_buffer);
171
172   memcpy(&current.whence, &ci->finish_time, sizeof(current.whence));
173   current.duration = duration.tv_sec * 1000 + duration.tv_usec / 1000;
174   current.available = (ci->timed_out || !ci->status.code) ? NP_UNAVAILABLE : NP_AVAILABLE;
175   current.state = (ci->status.code != 200) ? NP_BAD : NP_GOOD;
176   current.status = human_buffer;
177   if(current.available == NP_AVAILABLE) {
178     noit_stats_set_metric(&current, "code",
179                           METRIC_STRING, ci->status.code?code:NULL);
180     noit_stats_set_metric(&current, "bytes",
181                           METRIC_INT32, &ci->body.l);
182   }
183   else {
184     noit_stats_set_metric(&current, "code", METRIC_STRING, NULL);
185     noit_stats_set_metric(&current, "bytes", METRIC_INT32, NULL);
186   }
187   noit_check_set_stats(self, check, &current);
188 }
189 static void resmon_part_log_results_xml(noit_module_t *self,
190                                         noit_check_t *check,
191                                         xmlDocPtr xml) {
192   serf_check_info_t *ci = check->closure;
193   resmon_check_info_t *rci = check->closure;
194   xmlXPathContextPtr xpath_ctxt = NULL;
195   stats_t current;
196
197   noit_check_stats_clear(&current);
198   memcpy(&current.whence, &ci->finish_time, sizeof(current.whence));
199   current.available = NP_UNAVAILABLE;
200   current.state = NP_BAD;
201
202   if(xml && rci->xpathexpr) {
203     current.available = NP_AVAILABLE;
204     xpath_ctxt = xmlXPathNewContext(xml);
205     if(xpath_ctxt) {
206       xmlXPathObjectPtr pobj;
207       pobj = xmlXPathEval((xmlChar *)rci->xpathexpr, xpath_ctxt);
208       if(pobj) {
209         int i, cnt;
210         cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
211         for(i=0; i<cnt; i++) {
212           xmlNodePtr node;
213           char *value;
214           node = xmlXPathNodeSetItem(pobj->nodesetval, i);
215           value = (char *)xmlXPathCastNodeToString(node);
216           if(!strcmp((char *)node->name,"last_runtime_seconds")) {
217             float duration = atof(value) * 1000;
218             current.duration = (int) duration;
219           }
220           else if(!strcmp((char *)node->name, "message")) {
221             current.status = strdup(value);
222           }
223           else if(!strcmp((char *)node->name, "state")) {
224             current.state = strcmp(value,"OK") ? NP_BAD : NP_GOOD;
225           }
226         }
227       }
228       xmlXPathFreeContext(xpath_ctxt);
229     }
230   }
231   memcpy(&current.whence, &rci->serf.finish_time, sizeof(current.whence));
232   current.status = current.status ? current.status : strdup("unknown");
233   noitL(nldeb, "resmon_part(%s/%s/%s) [%s]\n", check->target,
234         rci->resmod, rci->resserv, current.status);
235   noit_check_set_stats(self, check, &current);
236 }
237 static void resmon_part_log_results(noit_module_t *self, noit_check_t *check,
238                                     noit_check_t *parent) {
239   resmon_check_info_t *rci = parent->closure;
240   resmon_part_log_results_xml(self, check, rci->xml_doc);
241 }
242 static void resmon_log_results(noit_module_t *self, noit_check_t *check) {
243   serf_check_info_t *ci = check->closure;
244   resmon_check_info_t *rci = check->closure;
245   struct timeval duration;
246   stats_t current;
247   int32_t services = 0;
248   char human_buffer[256], rt[14];
249   xmlDocPtr resmon_results = NULL;
250   xmlXPathContextPtr xpath_ctxt = NULL;
251   xmlXPathObjectPtr pobj = NULL;
252
253   noit_check_stats_clear(&current);
254
255   if(ci->body.b) resmon_results = xmlParseMemory(ci->body.b, ci->body.l);
256   if(resmon_results) {
257     xpath_ctxt = xmlXPathNewContext(resmon_results);
258     pobj = xmlXPathEval((xmlChar *)"/ResmonResults/ResmonResult", xpath_ctxt);
259     if(pobj)
260       if(pobj->type == XPATH_NODESET)
261         services = xmlXPathNodeSetGetLength(pobj->nodesetval);
262   } else {
263     if(ci->body.l)
264       noitL(nlerr, "Error in resmon doc: %s\n", ci->body.b);
265   }
266
267   /* Save our results for future dependent checks */
268   memcpy(&current.whence, &ci->finish_time, sizeof(current.whence));
269   memcpy(&rci->xml_doc_time, &ci->finish_time, sizeof(ci->finish_time));
270   if(rci->xml_doc) xmlFreeDoc(rci->xml_doc);
271   rci->xml_doc = resmon_results;
272
273   if(rci->xpathexpr) {
274     /* This is actually a part check... we had to do all the work as
275      * it isn't being used as a causal firing from a generic resmon check
276      */
277     resmon_part_log_results_xml(self, check, rci->xml_doc);
278     goto out;
279   }
280
281   sub_timeval(ci->finish_time, check->last_fire_time, &duration);
282   snprintf(rt, sizeof(rt), "%.3fs",
283            (float)duration.tv_sec + (float)duration.tv_usec / 1000000.0);
284   snprintf(human_buffer, sizeof(human_buffer),
285            "services=%d,rt=%s",
286            services,
287            ci->timed_out ? "timeout" : rt);
288   noitL(nldeb, "resmon(%s) [%s]\n", check->target, human_buffer);
289
290   current.duration = duration.tv_sec * 1000 + duration.tv_usec / 1000;
291   current.available = (ci->timed_out || ci->status.code != 200) ?
292                           NP_UNAVAILABLE : NP_AVAILABLE;
293   current.state = services ? NP_GOOD : NP_BAD;
294   current.status = human_buffer;
295
296   noit_stats_set_metric(&current, "services", METRIC_INT32, &services);
297   if(services) {
298     int i;
299     for(i=0; i<services; i++) {
300       xmlNodePtr node, attrnode;
301       node = xmlXPathNodeSetItem(pobj->nodesetval, i);
302       if(node) {
303         int a;
304         char *attrs[3] = { "last_runtime_seconds", "state", "message" };
305         char *resmod = NULL, *resserv = NULL, *value = NULL;
306         char attr[1024];
307         xmlXPathObjectPtr sobj;
308
309         xpath_ctxt->node = node;
310         sobj = xmlXPathEval((xmlChar *)"@module", xpath_ctxt);
311         resmod = (char *)xmlXPathCastNodeSetToString(sobj->nodesetval);
312         sobj = xmlXPathEval((xmlChar *)"@service", xpath_ctxt);
313         resserv = (char *)xmlXPathCastNodeSetToString(sobj->nodesetval);
314         if(!resmod && !resserv) continue;
315
316         for(a=0; a<3; a++) {
317           int32_t intval;
318           sobj = xmlXPathEval((xmlChar *)attrs[a], xpath_ctxt);
319           attrnode = xmlXPathNodeSetItem(sobj->nodesetval, 0);
320           value = (char *)xmlXPathCastNodeToString(attrnode);
321           snprintf(attr, sizeof(attr), "%s`%s`%s",
322                    resmod, resserv, (char *)attrnode->name);
323           switch(a) {
324             case 0:
325               /* The first is integer */
326               intval = (int)(atof(value) * 1000.0);
327               noit_stats_set_metric(&current, attr, METRIC_INT32, &intval);
328               break;
329             case 1:
330               noit_stats_set_metric(&current, attr, METRIC_STRING, value);
331               break;
332             case 2:
333               noit_stats_set_metric(&current, attr, METRIC_GUESS, value);
334               break;
335           }
336         }
337       }
338     }
339   }
340
341   noit_check_set_stats(self, check, &current);
342
343  out:
344   if(pobj) xmlXPathFreeObject(pobj);
345   if(xpath_ctxt) xmlXPathFreeContext(xpath_ctxt);
346 }
347 static void serf_cleanup(noit_module_t *self, noit_check_t *check) {
348   serf_check_info_t *ci;
349   ci = check->closure;
350   if(ci->connection) {
351     serf_connection_close(ci->connection);
352     ci->connection = NULL;
353   }
354   if(ci->fd_event) {
355     eventer_remove_fd(ci->fd_event->fd);
356     eventer_free(ci->fd_event);
357     ci->fd_event = NULL;
358   }
359   ci->timeout_event = NULL;
360   if(ci->pool) apr_pool_destroy(ci->pool);
361   memset(ci, 0, sizeof(*ci));
362 }
363 static int serf_complete(eventer_t e, int mask,
364                          void *closure, struct timeval *now) {
365   serf_closure_t *ccl = (serf_closure_t *)closure;
366
367   noitLT(nldeb, now, "serf_complete(%s)\n", ccl->check->target);
368   if(!NOIT_CHECK_DISABLED(ccl->check) && !NOIT_CHECK_KILLED(ccl->check)) {
369     serf_check_info_t *ci = ccl->check->closure;
370     memcpy(&ci->finish_time, now, sizeof(*now));
371     generic_log_results(ccl->self, ccl->check);
372   }
373   serf_cleanup(ccl->self, ccl->check);
374   ccl->check->flags &= ~NP_RUNNING;
375   free(ccl);
376   return 0;
377 }
378
379 static int serf_handler(eventer_t e, int mask,
380                         void *closure, struct timeval *now) {
381   apr_pollfd_t desc = { 0 };
382   serf_closure_t *sct = closure;
383   serf_check_info_t *ci = sct->check->closure;
384
385   desc.desc_type = APR_POLL_SOCKET;
386   desc.desc.s = sct->skt;
387
388   desc.rtnevents = 0;
389   if(mask & EVENTER_READ) desc.rtnevents |= APR_POLLIN;
390   if(mask & EVENTER_WRITE) desc.rtnevents |= APR_POLLOUT;
391   if(mask & EVENTER_EXCEPTION) desc.rtnevents |= APR_POLLERR;
392   serf_event_trigger(ci->context, sct->serf_baton, &desc);
393   serf_context_prerun(ci->context);
394
395   /* We're about to deschedule and free the event, drop our reference */
396   if(!e->mask)
397     ci->fd_event = NULL;
398
399   return e->mask;
400 }
401
402 static int serf_init(noit_module_t *self) {
403   return 0;
404 }
405 static void closed_connection(serf_connection_t *conn,
406                               void *closed_baton,
407                               apr_status_t why,
408                               apr_pool_t *pool) {
409 }
410
411 static serf_bucket_t* conn_setup(apr_socket_t *skt,
412                                 void *setup_baton,
413                                 apr_pool_t *pool) {
414   serf_bucket_t *c;
415   app_baton_t *ctx = setup_baton;
416
417   c = serf_bucket_socket_create(skt, ctx->bkt_alloc);
418   if (ctx->using_ssl) {
419       c = serf_bucket_ssl_decrypt_create(c, ctx->ssl_ctx, ctx->bkt_alloc);
420       if (!ctx->ssl_ctx) {
421           ctx->ssl_ctx = serf_bucket_ssl_decrypt_context_get(c);
422       }
423   }
424
425   return c;
426 }
427
428 static serf_bucket_t* accept_response(serf_request_t *request,
429                                       serf_bucket_t *stream,
430                                       void *acceptor_baton,
431                                       apr_pool_t *pool) {
432   serf_bucket_t *c;
433   serf_bucket_alloc_t *bkt_alloc;
434
435   /* get the per-request bucket allocator */
436   bkt_alloc = serf_request_get_alloc(request);
437
438   /* Create a barrier so the response doesn't eat us! */
439   c = serf_bucket_barrier_create(stream, bkt_alloc);
440
441   return serf_bucket_response_create(c, bkt_alloc);
442 }
443
444 static void append_buf(apr_pool_t *p, buf_t *b,
445                        const char *data, int len) {
446   char *n;
447   n = apr_palloc(p, b->l + len + 1);
448   if(b->l == 0)
449     b->b = n;
450   else {
451     memcpy(n, b->b, b->l);
452     b->b = n;
453   }
454   memcpy(b->b + b->l, data, len);
455   b->l += len;
456   b->b[b->l] = '\0';
457 }
458
459 static apr_status_t handle_response(serf_request_t *request,
460                                     serf_bucket_t *response,
461                                     void *handler_baton,
462                                     apr_pool_t *pool) {
463   const char *data;
464   apr_size_t len;
465   apr_status_t status;
466   handler_baton_t *ctx = handler_baton;
467   serf_check_info_t *ci = ctx->check->closure;
468
469   if(response == NULL) {
470     /* We were cancelled. */
471     goto finish;
472   }
473   status = serf_bucket_response_status(response, &ci->status);
474   if (status) {
475     if (APR_STATUS_IS_EAGAIN(status)) {
476       return status;
477     }
478     goto finish;
479   }
480
481   while (1) {
482     status = serf_bucket_read(response, 1024*32, &data, &len);
483     if (SERF_BUCKET_READ_ERROR(status))
484       return status;
485
486     append_buf(ci->pool, &ci->body, data, len);
487
488     /* are we done yet? */
489     if (APR_STATUS_IS_EOF(status)) {
490       serf_bucket_t *hdrs;
491       hdrs = serf_bucket_response_get_headers(response);
492       while (1) {
493         status = serf_bucket_read(hdrs, 2048, &data, &len);
494         if (SERF_BUCKET_READ_ERROR(status))
495           return status;
496
497         append_buf(ci->pool, &ci->headers, data, len);
498         if (APR_STATUS_IS_EOF(status)) {
499           break;
500         }
501       }
502
503       goto finish;
504     }
505
506     /* have we drained the response so far? */
507     if (APR_STATUS_IS_EAGAIN(status))
508       return status;
509
510     /* loop to read some more. */
511   }
512  finish:
513   gettimeofday(&ci->finish_time, NULL);
514   if(ci->timeout_event) {
515     eventer_remove(ci->timeout_event);
516     ci->timed_out = 0;
517     memcpy(&ci->timeout_event->whence, &ci->finish_time,
518            sizeof(&ci->finish_time));
519     eventer_add(ci->timeout_event);
520   }
521   return APR_EOF;
522 }
523
524 static apr_status_t setup_request(serf_request_t *request,
525                                   void *setup_baton,
526                                   serf_bucket_t **req_bkt,
527                                   serf_response_acceptor_t *acceptor,                                             void **acceptor_baton,
528                                   serf_response_handler_t *handler,
529                                   void **handler_baton,
530                                   apr_pool_t *pool) {
531   handler_baton_t *ctx = setup_baton;
532   serf_bucket_t *hdrs_bkt;
533   serf_bucket_t *body_bkt;
534
535   body_bkt = NULL;
536
537   *req_bkt = serf_bucket_request_create(ctx->method, ctx->path, body_bkt,
538                                         serf_request_get_alloc(request));
539
540   hdrs_bkt = serf_bucket_request_get_headers(*req_bkt);
541
542   serf_bucket_headers_setn(hdrs_bkt, "Host", ctx->host);
543   serf_bucket_headers_setn(hdrs_bkt, "User-Agent",
544                            "Noit/" NOIT_HTTP_VERSION_STRING);
545   /* Shouldn't serf do this for us? */
546   serf_bucket_headers_setn(hdrs_bkt, "Accept-Encoding", "gzip");
547
548   if (ctx->authn != NULL) {
549     serf_bucket_headers_setn(hdrs_bkt, "Authorization", ctx->authn);
550   }
551
552   if (ctx->acceptor_baton->using_ssl) {
553     serf_bucket_alloc_t *req_alloc;
554     app_baton_t *app_ctx = ctx->acceptor_baton;
555
556     req_alloc = serf_request_get_alloc(request);
557
558     if (app_ctx->ssl_ctx == NULL) {
559       *req_bkt =
560         serf_bucket_ssl_encrypt_create(*req_bkt, NULL,
561                                        app_ctx->bkt_alloc);
562       app_ctx->ssl_ctx =
563         serf_bucket_ssl_encrypt_context_get(*req_bkt);
564     }
565     else {
566       *req_bkt =
567         serf_bucket_ssl_encrypt_create(*req_bkt, app_ctx->ssl_ctx,
568                                        app_ctx->bkt_alloc);
569     }
570   }
571
572   *acceptor = ctx->acceptor;
573   *acceptor_baton = ctx->acceptor_baton;
574   *handler = ctx->handler;
575   *handler_baton = ctx;
576
577   return APR_SUCCESS;
578 }
579
580 struct __unix_apr_socket_t {
581   apr_pool_t *pool;
582   int socketdes;
583 };
584
585 static apr_status_t serf_eventer_add(void *user_baton,
586                                      apr_pollfd_t *pfd,
587                                      void *serf_baton) {
588   eventer_t e, newe = NULL;
589   serf_closure_t *sct = user_baton, *newsct;
590   assert(pfd->desc_type == APR_POLL_SOCKET);
591   struct __unix_apr_socket_t *hack = (struct __unix_apr_socket_t *)pfd->desc.s;
592
593   noitL(nldeb, "serf_eventer_add() => %d\n", hack->socketdes);
594   e = eventer_find_fd(hack->socketdes);
595   if(!e) {
596     newe = e = eventer_alloc();
597     e->fd = hack->socketdes;
598     e->callback = serf_handler;
599     e->closure = calloc(1, sizeof(serf_closure_t));
600   }
601   newsct = e->closure;
602   newsct->self = sct->self;
603   newsct->check = sct->check;
604   newsct->serf_baton = serf_baton;
605   newsct->skt = pfd->desc.s;
606   e->mask = 0;
607   if(pfd->reqevents & APR_POLLIN) e->mask |= EVENTER_READ;
608   if(pfd->reqevents & APR_POLLOUT) e->mask |= EVENTER_WRITE;
609   if(pfd->reqevents & APR_POLLERR) e->mask |= EVENTER_EXCEPTION;
610   if(newe) {
611     serf_check_info_t *ci = sct->check->closure;
612     eventer_add(newe);
613     ci->fd_event = newe;
614   }
615 /* ** Unneeded as this is called recursively **
616   else
617     eventer_update(e);
618 */
619   return APR_SUCCESS;
620 }
621 static apr_status_t serf_eventer_remove(void *user_baton,
622                                         apr_pollfd_t *pfd,
623                                         void *serf_baton) {
624   serf_closure_t *sct = user_baton;
625   serf_check_info_t *ci;
626   eventer_t e;
627
628   ci = sct->check->closure;
629   assert(pfd->desc_type == APR_POLL_SOCKET);
630   struct __unix_apr_socket_t *hack = (struct __unix_apr_socket_t *)pfd->desc.s;
631
632   noitL(nldeb, "serf_eventer_remove() => %d\n", hack->socketdes);
633   e = eventer_find_fd(hack->socketdes);
634   if(e) e->mask = 0;
635   return 0;
636 }
637
638 static int serf_initiate(noit_module_t *self, noit_check_t *check) {
639   serf_closure_t *ccl;
640   serf_check_info_t *ci;
641   struct timeval when, p_int;
642   apr_status_t status;
643   eventer_t newe;
644   serf_module_conf_t *mod_config;
645   char *config_url;
646
647   mod_config = noit_module_get_userdata(self);
648   ci = (serf_check_info_t *)check->closure;
649   /* We cannot be running */
650   assert(!(check->flags & NP_RUNNING));
651   check->flags |= NP_RUNNING;
652   noitL(nldeb, "serf_initiate(%p,%s)\n",
653         self, check->target);
654
655   /* remove a timeout if we still have one -- we should unless someone
656    * has set a lower timeout than the period.
657    */
658   ci->timed_out = 1;
659   if(ci->timeout_event) {
660     eventer_remove(ci->timeout_event);
661     free(ci->timeout_event->closure);
662     eventer_free(ci->timeout_event);
663     ci->timeout_event = NULL;
664   }
665   assert(!ci->pool);
666   apr_pool_create(&ci->pool, NULL);
667   apr_atomic_init(ci->pool);
668
669   gettimeofday(&when, NULL);
670   memcpy(&check->last_fire_time, &when, sizeof(when));
671
672   ccl = apr_pcalloc(ci->pool, sizeof(*ccl));
673   ccl->self = self;
674   ccl->check = check;
675
676   if(!noit_hash_retrieve(check->config, "url", strlen("url"),
677                         (void **)&config_url))
678     if(!mod_config->options ||
679        !noit_hash_retrieve(mod_config->options, "url", strlen("url"),
680                            (void **)&config_url))
681       config_url = "http://localhost/";
682   apr_uri_parse(ci->pool, config_url, &ci->url);
683
684   if (!ci->url.port) {
685     ci->url.port = apr_uri_port_of_scheme(ci->url.scheme);
686   }
687   if (!ci->url.path) {
688     ci->url.path = "/";
689   }
690
691   if (strcasecmp(ci->url.scheme, "https") == 0) {
692     ci->app_ctx.using_ssl = 1;
693   }
694   else {
695     ci->app_ctx.using_ssl = 0;
696   }
697
698   status = apr_sockaddr_info_get(&ci->address,
699                                  check->target, APR_UNSPEC, ci->url.port, 0,
700                                  ci->pool);
701   if (status) {
702     /* Handle error -- log failure */
703     apr_pool_destroy(ci->pool);
704     memset(ci, 0, sizeof(*ci));
705     check->flags &= ~NP_RUNNING;
706     return 0;
707   }
708
709   ci->context = serf_context_create_ex(ccl, serf_eventer_add,
710                                        serf_eventer_remove, ci->pool);
711
712   ci->app_ctx.bkt_alloc = serf_bucket_allocator_create(ci->pool, NULL, NULL);
713   ci->app_ctx.ssl_ctx = NULL;
714
715   ci->connection = serf_connection_create(ci->context, ci->address,
716                                           conn_setup, &ci->app_ctx,
717                                           closed_connection, &ci->app_ctx,
718                                           ci->pool);
719
720   ci->handler_ctx.method = apr_pstrdup(ci->pool, "GET");
721   ci->handler_ctx.host = apr_pstrdup(ci->pool, ci->url.hostname);
722   ci->handler_ctx.path = ci->url.path;
723   ci->handler_ctx.authn = NULL;
724
725   ci->handler_ctx.acceptor = accept_response;
726   ci->handler_ctx.acceptor_baton = &ci->app_ctx;
727   ci->handler_ctx.handler = handle_response;
728   ci->handler_ctx.self = self;
729   ci->handler_ctx.check = check;
730
731   ci->request = serf_connection_request_create(ci->connection, setup_request,
732                                                &ci->handler_ctx);
733   serf_context_prerun(ci->context);
734
735   newe = eventer_alloc();
736   newe->mask = EVENTER_TIMER;
737   gettimeofday(&when, NULL);
738   p_int.tv_sec = check->timeout / 1000;
739   p_int.tv_usec = (check->timeout % 1000) * 1000;
740   add_timeval(when, p_int, &newe->whence);
741   ccl = calloc(1, sizeof(*ccl));
742   ccl->self = self;
743   ccl->check = check;
744   newe->closure = ccl;
745   newe->callback = serf_complete;
746   eventer_add(newe);
747   ci->timeout_event = newe;
748   return 0;
749 }
750 static int serf_initiate_check(noit_module_t *self, noit_check_t *check,
751                                int once, noit_check_t *cause) {
752   if(!check->closure) check->closure = calloc(1, sizeof(serf_check_info_t));
753   INITIATE_CHECK(serf_initiate, self, check);
754   return 0;
755 }
756 static int resmon_initiate_check(noit_module_t *self, noit_check_t *check,
757                                  int once, noit_check_t *parent) {
758   /* resmon_check_info_t gives us a bit more space */
759   if(!check->closure) check->closure = calloc(1, sizeof(resmon_check_info_t));
760   INITIATE_CHECK(serf_initiate, self, check);
761   return 0;
762 }
763
764 static void resmon_cleanup(noit_module_t *self, noit_check_t *check) {
765   resmon_check_info_t *rci;
766   rci = check->closure;
767   if(rci) {
768     if(rci->xpathexpr) free(rci->xpathexpr);
769     if(rci->resmod) free(rci->resmod);
770     if(rci->resserv) free(rci->resserv);
771     if(rci->xml_doc) xmlFreeDoc(rci->xml_doc);
772     serf_cleanup(self, check);
773   }
774 }
775 static int resmon_part_initiate_check(noit_module_t *self, noit_check_t *check,
776                                       int once, noit_check_t *parent) {
777   char xpathexpr[1024];
778   const char *resmod, *resserv;
779   resmon_check_info_t *rci;
780
781   if(NOIT_CHECK_DISABLED(check) || NOIT_CHECK_KILLED(check)) return 0;
782
783   if(!check->closure) check->closure = calloc(1, sizeof(resmon_check_info_t));
784   rci = check->closure;
785   if(!rci->xpathexpr) {
786     if(!noit_hash_retrieve(check->config,
787                            "resmon_module", strlen("resmon_module"),
788                            (void **)&resmod)) {
789       resmod = "DUMMY_MODULE";
790     }
791     if(!noit_hash_retrieve(check->config,
792                            "resmon_service", strlen("resmon_service"),
793                            (void **)&resserv)) {
794       resserv = "DUMMY_SERVICE";
795     }
796     snprintf(xpathexpr, sizeof(xpathexpr),
797              "//ResmonResult[@module=\"%s\" and @service=\"%s\"]/*",
798              resmod, resserv);
799     rci->xpathexpr = strdup(xpathexpr);
800     rci->resmod = strdup(resmod);
801     rci->resserv = strdup(resserv);
802   }
803
804   if(parent && !strcmp(parent->module, "resmon")) {
805     /* Content is cached in the parent */
806     serf_check_info_t *ci = (serf_check_info_t *)rci;
807     gettimeofday(&ci->finish_time, NULL);
808     resmon_part_log_results(self, check, parent);
809     return 0;
810   }
811   INITIATE_CHECK(serf_initiate, self, check);
812   return 0;
813 }
814
815 static int serf_onload(noit_module_t *self) {
816   apr_initialize();
817   atexit(apr_terminate);
818
819   nlerr = noit_log_stream_find("error/serf");
820   nldeb = noit_log_stream_find("debug/serf");
821   if(!nlerr) nlerr = noit_stderr;
822   if(!nldeb) nldeb = noit_debug;
823
824   eventer_name_callback("http/serf_handler", serf_handler);
825   eventer_name_callback("http/serf_complete", serf_complete);
826   return 0;
827 }
828 noit_module_t http = {
829   NOIT_MODULE_MAGIC,
830   NOIT_MODULE_ABI_VERSION,
831   "http",
832   "libserf-based HTTP and HTTPS resource checker",
833   serf_onload,
834   serf_config,
835   serf_init,
836   serf_initiate_check,
837   serf_cleanup
838 };
839
840 noit_module_t resmon = {
841   NOIT_MODULE_MAGIC,
842   NOIT_MODULE_ABI_VERSION,
843   "resmon",
844   "libserf-based resmon resource checker",
845   serf_onload,
846   resmon_config,
847   serf_init,
848   resmon_initiate_check,
849   resmon_cleanup
850 };
851
852 noit_module_t resmon_part = {
853   NOIT_MODULE_MAGIC,
854   NOIT_MODULE_ABI_VERSION,
855   "resmon_part",
856   "resmon part resource checker",
857   serf_onload,
858   resmon_config,
859   serf_init,
860   resmon_part_initiate_check,
861   resmon_cleanup
862 };
863
Note: See TracBrowser for help on using the browser.