root/src/modules/http.c

Revision 7a82302e42af9b267a7e01baf5413e332772f261, 28.4 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 6 years ago)

add cert validation hooks

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