root/src/modules/http.c

Revision 4f91a9df3464c7910b541eb625d8bbb3e11b0058, 29.2 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 5 years ago)

This bug totally sucks. I hate C. I love you C.

This short copy would cause the check to report an erroneously high duration
(to the tune of the lesser of half the period of the check or 1 second).

closes #88

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