root/src/noit_check_rest.c

Revision b553f9a4d73487ee85e5cfedf2cd67352b4d5e6b, 21.6 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 3 years ago)

refs #340

This is a rather major change. Targets can now be hostnames in addition
to IP addresses so long as //checks/@resolve_targets is not false.

If a target is entered that does not look like an IP (inet_pton fails)
then the check is marked as needing resolution NP_RESOLVE.

A passive look-aside dns cache has been implemented in noit_check_resolver.c
that is used to power the whole system and some reasonably simply console
command have been provided:

show dns_cache [fqdn1 [fqdn2]] -- shows the state
dns_cache <fqdn> [fqdn2 [fqdn3]] -- submits for lookup
no dns_cache <fqdn> [fqdn2 [fqdn3]] -- purges from cache

The big change is that modules that relied on check->target to be an IP
address are going to explode when names are provided. Instead, modules
should now use target for the provided target (possibly a FQDN) and use
target_ip (check->target_ip or check.target_ip) for a resolved IP address
and also check for the case of empty string: (check->target_ip[0] == '\0')
for the case that resolution has failed. In lua, the target_ip will be
presented as nil in the case of failed name resolution.

I believe I've updated all necessary components of the system for this to
"just work" but people that have implemented their own check should update
them before they elect to use non-IP addresses as targets.

The dns subsystem supports both IPv4 and IPv6, but currently prefers IPv4
addresses if any are present.

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2009, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  *       notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  *       copyright notice, this list of conditions and the following
13  *       disclaimer in the documentation and/or other materials provided
14  *       with the distribution.
15  *     * Neither the name OmniTI Computer Consulting, Inc. nor the names
16  *       of its contributors may be used to endorse or promote products
17  *       derived from this software without specific prior written
18  *       permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include "noit_defines.h"
34 #include <assert.h>
35 #include <errno.h>
36 #include <libxml/parser.h>
37 #include <libxml/tree.h>
38 #include <libxml/xpath.h>
39 #include "noit_listener.h"
40 #include "noit_http.h"
41 #include "noit_rest.h"
42 #include "noit_check.h"
43 #include "noit_check_tools.h"
44 #include "noit_conf.h"
45 #include "noit_conf_private.h"
46 #include "noit_filters.h"
47
48 #define FAIL(a) do { error = (a); goto error; } while(0)
49
50 #define NODE_CONTENT(parent, k, v) do { \
51   xmlNodePtr tmp; \
52   if(v) { \
53     tmp = xmlNewNode(NULL, (xmlChar *)(k)); \
54     xmlNodeAddContent(tmp, (xmlChar *)(v)); \
55     xmlAddChild(parent, tmp); \
56   } \
57 } while(0)
58
59 xmlNodePtr
60 noit_check_state_as_xml(noit_check_t *check) {
61   xmlNodePtr state, tmp, metrics;
62   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
63   const char *k;
64   int klen;
65   void *data;
66   stats_t *c = &check->stats.current;
67
68   state = xmlNewNode(NULL, (xmlChar *)"state");
69   NODE_CONTENT(state, "running", NOIT_CHECK_RUNNING(check)?"true":"false");
70   NODE_CONTENT(state, "killed", NOIT_CHECK_KILLED(check)?"true":"false");
71   NODE_CONTENT(state, "configured",
72                NOIT_CHECK_CONFIGURED(check)?"true":"false");
73   NODE_CONTENT(state, "disabled", NOIT_CHECK_DISABLED(check)?"true":"false");
74   NODE_CONTENT(state, "target_ip", check->target_ip);
75   xmlAddChild(state, (tmp = xmlNewNode(NULL, (xmlChar *)"last_run")));
76   if(check->stats.current.whence.tv_sec) {
77     struct timeval f = check->stats.current.whence;
78     struct timeval n;
79     char timestr[20];
80     gettimeofday(&n, NULL);
81     snprintf(timestr, sizeof(timestr), "%0.3f",
82              n.tv_sec + (n.tv_usec / 1000000.0));
83     xmlSetProp(tmp, (xmlChar *)"now", (xmlChar *)timestr);
84     snprintf(timestr, sizeof(timestr), "%0.3f",
85              f.tv_sec + (f.tv_usec / 1000000.0));
86     xmlNodeAddContent(tmp, (xmlChar *)timestr);
87   }
88   if(c->available) { /* truth here means the check has been run */
89     char buff[20], *compiler_warning;
90     snprintf(buff, sizeof(buff), "%0.3f", (float)c->duration/1000.0);
91     compiler_warning = buff;
92     NODE_CONTENT(state, "runtime", compiler_warning);
93   }
94   NODE_CONTENT(state, "availability",
95                noit_check_available_string(c->available));
96   NODE_CONTENT(state, "state", noit_check_state_string(c->state));
97   NODE_CONTENT(state, "status", c->status ? c->status : "");
98   memset(&iter, 0, sizeof(iter));
99   xmlAddChild(state, (metrics = xmlNewNode(NULL, (xmlChar *)"metrics")));
100   while(noit_hash_next(&c->metrics, &iter, &k, &klen, &data)) {
101     char buff[256];
102     metric_t *m = (metric_t *)data;
103     xmlAddChild(metrics, (tmp = xmlNewNode(NULL, (xmlChar *)"metric")));
104     xmlSetProp(tmp, (xmlChar *)"name", (xmlChar *)m->metric_name);
105     buff[0] = m->metric_type; buff[1] = '\0';
106     xmlSetProp(tmp, (xmlChar *)"type", (xmlChar *)buff);
107     if(m->metric_value.s) {
108       int rv;
109       rv = noit_stats_snprint_metric_value(buff, sizeof(buff), m);
110       if(rv < 0)
111         xmlSetProp(tmp, (xmlChar *)"error", (xmlChar *)"unknown type");
112       else
113         xmlNodeAddContent(tmp, (xmlChar *)buff);
114     }
115   }
116   return state;
117 }
118
119 static int
120 rest_show_check(noit_http_rest_closure_t *restc,
121                 int npats, char **pats) {
122   noit_http_session_ctx *ctx = restc->http_ctx;
123   xmlXPathObjectPtr pobj = NULL;
124   xmlXPathContextPtr xpath_ctxt = NULL;
125   xmlDocPtr doc = NULL;
126   xmlNodePtr node, root, attr, config, state, tmp, anode;
127   uuid_t checkid;
128   noit_check_t *check;
129   char xpath[1024], *uuid_conf, *module, *value;
130   int rv, cnt, error_code = 500;
131   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
132   const char *k;
133   int klen;
134   void *data;
135   noit_hash_table *configh;
136
137   if(npats != 2) goto error;
138
139   rv = noit_check_xpath(xpath, sizeof(xpath), pats[0], pats[1]);
140   if(rv == 0) goto not_found;
141   if(rv < 0) goto error;
142
143   noit_conf_xml_xpath(NULL, &xpath_ctxt);
144   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
145   if(!pobj || pobj->type != XPATH_NODESET ||
146      xmlXPathNodeSetIsEmpty(pobj->nodesetval)) goto not_found;
147   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
148   if(cnt != 1) goto error;
149
150   node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
151   uuid_conf = (char *)xmlGetProp(node, (xmlChar *)"uuid");
152   if(!uuid_conf || uuid_parse(uuid_conf, checkid)) goto error;
153
154   doc = xmlNewDoc((xmlChar *)"1.0");
155   root = xmlNewDocNode(doc, NULL, (xmlChar *)"check", NULL);
156   xmlDocSetRootElement(doc, root);
157
158 #define MYATTR(node,a,n,b) _noit_conf_get_string(node, &(n), "@" #a, &(b))
159 #define INHERIT(node,a,n,b) \
160   _noit_conf_get_string(node, &(n), "ancestor-or-self::node()/@" #a, &(b))
161 #define SHOW_ATTR(parent, node, a) do { \
162   xmlNodePtr anode = NULL; \
163   char *value = NULL; \
164   INHERIT(node, a, anode, value); \
165   if(value != NULL) { \
166     int clen, plen;\
167     const char *cpath, *apath; \
168     xmlNodePtr child; \
169     cpath = node ? (char *)xmlGetNodePath(node) : ""; \
170     apath = anode ? (char *)xmlGetNodePath(anode) : ""; \
171     clen = strlen(cpath); \
172     plen = strlen("/noit/checks"); \
173     child = xmlNewNode(NULL, (xmlChar *)#a); \
174     xmlNodeAddContent(child, (xmlChar *)value); \
175     if(!strncmp(cpath, apath, clen) && apath[clen] == '/') { \
176     } \
177     else { \
178       xmlSetProp(child, (xmlChar *)"inherited", (xmlChar *)apath+plen); \
179     } \
180     xmlAddChild(parent, child); \
181   } \
182 } while(0)
183
184   attr = xmlNewNode(NULL, (xmlChar *)"attributes");
185   xmlAddChild(root, attr);
186
187   SHOW_ATTR(attr,node,uuid);
188
189   /* Name is odd, it falls back transparently to module */
190   if(!INHERIT(node, module, tmp, module)) module = NULL;
191   xmlAddChild(attr, (tmp = xmlNewNode(NULL, (xmlChar *)"name")));
192   if(MYATTR(node, name, anode, value))
193     xmlNodeAddContent(tmp, (xmlChar *)value);
194   else if(module)
195     xmlNodeAddContent(tmp, (xmlChar *)module);
196
197   SHOW_ATTR(attr,node,module);
198   SHOW_ATTR(attr,node,target);
199   SHOW_ATTR(attr,node,period);
200   SHOW_ATTR(attr,node,timeout);
201   SHOW_ATTR(attr,node,oncheck);
202   SHOW_ATTR(attr,node,filterset);
203   SHOW_ATTR(attr,node,disable);
204
205   /* Add the config */
206   config = xmlNewNode(NULL, (xmlChar *)"config");
207   configh = noit_conf_get_hash(node, "config");
208   while(noit_hash_next(configh, &iter, &k, &klen, &data))
209     NODE_CONTENT(config, k, data);
210   noit_hash_destroy(configh, free, free);
211   free(configh);
212   xmlAddChild(root, config);
213
214   /* Add the state */
215   check = noit_poller_lookup(checkid);
216   if(!check) {
217     state = xmlNewNode(NULL, (xmlChar *)"state");
218     xmlSetProp(state, (xmlChar *)"error", (xmlChar *)"true");
219   }
220   else
221     state = noit_check_state_as_xml(check);
222   xmlAddChild(root, state);
223   noit_http_response_ok(ctx, "text/xml");
224   noit_http_response_xml(ctx, doc);
225   noit_http_response_end(ctx);
226   goto cleanup;
227
228  not_found:
229   noit_http_response_not_found(ctx, "text/html");
230   noit_http_response_end(ctx);
231   goto cleanup;
232
233  error:
234   noit_http_response_standard(ctx, error_code, "ERROR", "text/html");
235   noit_http_response_end(ctx);
236   goto cleanup;
237
238  cleanup:
239   if(pobj) xmlXPathFreeObject(pobj);
240   if(doc) xmlFreeDoc(doc);
241   return 0;
242 }
243
244 int
245 noit_validate_check_rest_post(xmlDocPtr doc, xmlNodePtr *a, xmlNodePtr *c,
246                               const char **error) {
247   xmlNodePtr root, tl, an;
248   int name=0, module=0, target=0, period=0, timeout=0, filterset=0;
249   *a = *c = NULL;
250   root = xmlDocGetRootElement(doc);
251   if(!root || strcmp((char *)root->name, "check")) return 0;
252   for(tl = root->children; tl; tl = tl->next) {
253     if(!strcmp((char *)tl->name, "attributes")) {
254       *a = tl;
255       for(an = tl->children; an; an = an->next) {
256 #define CHECK_N_SET(a) if(!strcmp((char *)an->name, #a))
257         CHECK_N_SET(name) {
258           xmlChar *tmp;
259           pcre *valid_name = noit_conf_get_valid_name_checker();
260           int ovector[30], valid;
261           tmp = xmlNodeGetContent(an);
262           valid = (pcre_exec(valid_name, NULL,
263                              (char *)tmp, strlen((char *)tmp), 0, 0,
264                              ovector, sizeof(ovector)/sizeof(*ovector)) > 0);
265           xmlFree(tmp);
266           if(!valid) { *error = "invalid name"; return 0; }
267           name = 1;
268         }
269         else CHECK_N_SET(module) module = 1; /* This is validated by called */
270         else CHECK_N_SET(target) {
271           noit_boolean should_resolve;
272           int valid;
273           xmlChar *tmp;
274           tmp = xmlNodeGetContent(an);
275           valid = noit_check_is_valid_target((char *)tmp);
276           xmlFree(tmp);
277           if(noit_conf_get_boolean(NULL, "//checks/@resolve_targets",
278                                    &should_resolve) &&
279              should_resolve == noit_false &&
280              !valid) {
281             *error = "invalid target";
282             return 0;
283           }
284           target = 1;
285         }
286         else CHECK_N_SET(period) {
287           int pint;
288           xmlChar *tmp;
289           tmp = xmlNodeGetContent(an);
290           pint = noit_conf_string_to_int((char *)tmp);
291           xmlFree(tmp);
292           if(pint < 5000 || pint > 300000) {
293             *error = "invalid period";
294             return 0;
295           }
296           period = 1;
297         }
298         else CHECK_N_SET(timeout) {
299           int pint;
300           xmlChar *tmp;
301           tmp = xmlNodeGetContent(an);
302           pint = noit_conf_string_to_int((char *)tmp);
303           xmlFree(tmp);
304           if(pint < 0 || pint > 300000) {
305             *error = "invalid timeout";
306             return 0;
307           }
308           timeout = 1;
309         }
310         else CHECK_N_SET(filterset) {
311           xmlChar *tmp;
312           tmp = xmlNodeGetContent(an);
313           if(!noit_filter_exists((char *)tmp)) {
314             *error = "filterset does not exist";
315             return 0;
316           }
317           filterset = 1;
318         }
319         else CHECK_N_SET(disable) { /* not required */
320           int valid;
321           xmlChar *tmp;
322           tmp = xmlNodeGetContent(an);
323           valid = (!strcasecmp((char *)tmp, "true") ||
324                    !strcasecmp((char *)tmp, "on") ||
325                    !strcasecmp((char *)tmp, "false") ||
326                    !strcasecmp((char *)tmp, "off"));
327           xmlFree(tmp);
328           if(!valid) { *error = "bad disable parameter"; return 0; }
329           target = 1;
330         }
331         else return 0;
332       }
333     }
334     else if(!strcmp((char *)tl->name, "config")) {
335       *c = tl;
336       /* Noop, anything goes */
337     }
338     else return 0;
339   }
340   if(name && module && target && period && timeout && filterset) return 1;
341   *error = "insufficient information";
342   return 0;
343 }
344 static void
345 configure_xml_check(xmlNodePtr check, xmlNodePtr a, xmlNodePtr c) {
346   xmlNodePtr n, config, oldconfig;
347   for(n = a->children; n; n = n->next) {
348 #define ATTR2PROP(attr) do { \
349   if(!strcmp((char *)n->name, #attr)) { \
350     xmlChar *v = xmlNodeGetContent(n); \
351     if(v) xmlSetProp(check, n->name, v); \
352     else xmlUnsetProp(check, n->name); \
353     if(v) xmlFree(v); \
354   } \
355 } while(0)
356     ATTR2PROP(name);
357     ATTR2PROP(target);
358     ATTR2PROP(module);
359     ATTR2PROP(period);
360     ATTR2PROP(timeout);
361     ATTR2PROP(disable);
362     ATTR2PROP(filterset);
363   }
364   for(oldconfig = check->children; oldconfig; oldconfig = oldconfig->next)
365     if(!strcmp((char *)oldconfig->name, "config")) break;
366   config = xmlNewNode(NULL, (xmlChar *)"config");
367   if(c) {
368     xmlAttrPtr inherit;
369     if((inherit = xmlHasProp(c, (xmlChar *)"inherit")) != NULL &&
370         inherit->children && inherit->children->content)
371       xmlSetProp(config, (xmlChar *)"inherit", inherit->children->content);
372     for(n = c->children; n; n = n->next) {
373       xmlChar *v = xmlNodeGetContent(n);
374       xmlNodePtr co = xmlNewNode(NULL, n->name);
375       xmlNodeAddContent(co, v);
376       xmlFree(v);
377       xmlAddChild(config, co);
378     }
379   }
380   if(oldconfig) {
381     xmlReplaceNode(oldconfig, config);
382     xmlFreeNode(oldconfig);
383   }
384   else xmlAddChild(check, config);
385 }
386 static xmlNodePtr
387 make_conf_path(char *path) {
388   xmlNodePtr start, tmp;
389   char fullpath[1024], *tok, *brk;
390   if(!path || strlen(path) < 1) return NULL;
391   snprintf(fullpath, sizeof(fullpath), "%s", path+1);
392   fullpath[strlen(fullpath)-1] = '\0';
393   start = noit_conf_get_section(NULL, "/noit/checks");
394   if(!start) return NULL;
395   for (tok = strtok_r(fullpath, "/", &brk);
396        tok;
397        tok = strtok_r(NULL, "/", &brk)) {
398     if(!xmlValidateNameValue((xmlChar *)tok)) return NULL;
399     if(!strcmp(tok, "check")) return NULL;  /* These two paths */
400     if(!strcmp(tok, "config")) return NULL; /* are off limits. */
401     for (tmp = start->children; tmp; tmp = tmp->next) {
402       if(!strcmp((char *)tmp->name, tok)) break;
403     }
404     if(!tmp) {
405       tmp = xmlNewNode(NULL, (xmlChar *)tok);
406       xmlAddChild(start, tmp);
407     }
408     start = tmp;
409   }
410   return start;
411 }
412 static int
413 rest_delete_check(noit_http_rest_closure_t *restc,
414                   int npats, char **pats) {
415   noit_http_session_ctx *ctx = restc->http_ctx;
416   xmlXPathObjectPtr pobj = NULL;
417   xmlXPathContextPtr xpath_ctxt = NULL;
418   xmlNodePtr node;
419   uuid_t checkid;
420   noit_check_t *check;
421   const char *error;
422   char xpath[1024], *uuid_conf;
423   int rv, cnt, error_code = 500;
424   noit_boolean exists = noit_false;
425
426   if(npats != 2) goto error;
427
428   if(uuid_parse(pats[1], checkid)) goto error;
429   check = noit_poller_lookup(checkid);
430   if(check)
431     exists = noit_true;
432
433   rv = noit_check_xpath(xpath, sizeof(xpath), pats[0], pats[1]);
434   if(rv == 0) FAIL("uuid not valid");
435   if(rv < 0) FAIL("Tricky McTrickster... No");
436
437   noit_conf_xml_xpath(NULL, &xpath_ctxt);
438   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
439   if(!pobj || pobj->type != XPATH_NODESET ||
440      xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
441     if(exists) { error_code = 403; FAIL("uuid not yours"); }
442     goto not_found;
443   }
444   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
445   if(cnt != 1) FAIL("internal error, |checkid| > 1");
446   node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
447   uuid_conf = (char *)xmlGetProp(node, (xmlChar *)"uuid");
448   if(!uuid_conf || strcasecmp(uuid_conf, pats[1]))
449     FAIL("internal error uuid");
450
451   /* delete this here */
452   noit_poller_deschedule(check->checkid);
453   xmlUnlinkNode(node);
454   xmlFreeNode(node);
455   if(noit_conf_write_file(NULL) != 0)
456     noitL(noit_error, "local config write failed\n");
457   noit_conf_mark_changed();
458   noit_http_response_ok(ctx, "text/html");
459   noit_http_response_end(ctx);
460   goto cleanup;
461
462  not_found:
463   noit_http_response_not_found(ctx, "text/html");
464   noit_http_response_end(ctx);
465   goto cleanup;
466
467  error:
468   noit_http_response_standard(ctx, error_code, "ERROR", "text/html");
469   noit_http_response_end(ctx);
470   goto cleanup;
471
472  cleanup:
473   if(pobj) xmlXPathFreeObject(pobj);
474   return 0;
475 }
476
477 static int
478 rest_set_check(noit_http_rest_closure_t *restc,
479                int npats, char **pats) {
480   noit_http_session_ctx *ctx = restc->http_ctx;
481   xmlXPathObjectPtr pobj = NULL;
482   xmlXPathContextPtr xpath_ctxt = NULL;
483   xmlDocPtr doc = NULL, indoc = NULL;
484   xmlNodePtr node, root, attr, config, parent;
485   uuid_t checkid;
486   noit_check_t *check;
487   char xpath[1024], *uuid_conf;
488   int rv, cnt, error_code = 500, complete = 0, mask = 0;
489   const char *error = "internal error";
490   noit_boolean exists = noit_false;
491
492   if(npats != 2) goto error;
493
494   indoc = rest_get_xml_upload(restc, &mask, &complete);
495   if(!complete) return mask;
496   if(indoc == NULL) FAIL("xml parse error");
497   if(!noit_validate_check_rest_post(indoc, &attr, &config, &error)) goto error;
498
499   if(uuid_parse(pats[1], checkid)) goto error;
500   check = noit_poller_lookup(checkid);
501   if(check)
502     exists = noit_true;
503
504   rv = noit_check_xpath(xpath, sizeof(xpath), pats[0], pats[1]);
505   if(rv == 0) FAIL("uuid not valid");
506   if(rv < 0) FAIL("Tricky McTrickster... No");
507
508   noit_conf_xml_xpath(NULL, &xpath_ctxt);
509   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
510   if(!pobj || pobj->type != XPATH_NODESET ||
511      xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
512     if(exists) { error_code = 403; FAIL("uuid not yours"); }
513     else {
514       char *target = NULL, *name = NULL, *module = NULL;
515       noit_module_t *m;
516       xmlNodePtr newcheck, a;
517       /* make sure this isn't a dup */
518       for(a = attr->children; a; a = a->next) {
519         if(!strcmp((char *)a->name, "target"))
520           target = (char *)xmlNodeGetContent(a);
521         if(!strcmp((char *)a->name, "name"))
522           name = (char *)xmlNodeGetContent(a);
523         if(!strcmp((char *)a->name, "module"))
524           module = (char *)xmlNodeGetContent(a);
525       }
526       exists = (noit_poller_lookup_by_name(target, name) != NULL);
527       m = noit_module_lookup(module);
528       xmlFree(target);
529       xmlFree(name);
530       xmlFree(module);
531       if(exists) FAIL("target`name already registered");
532       if(!m) FAIL("module does not exist");
533       /* create a check here */
534       newcheck = xmlNewNode(NULL, (xmlChar *)"check");
535       xmlSetProp(newcheck, (xmlChar *)"uuid", (xmlChar *)pats[1]);
536       configure_xml_check(newcheck, attr, config);
537       parent = make_conf_path(pats[0]);
538       if(!parent) FAIL("invalid path");
539       xmlAddChild(parent, newcheck);
540     }
541   }
542   if(exists) {
543     int module_change;
544     char *target = NULL, *name = NULL, *module = NULL;
545     xmlNodePtr a;
546     noit_check_t *ocheck;
547
548     cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
549     if(cnt != 1) FAIL("internal error, |checkid| > 1");
550     node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
551     uuid_conf = (char *)xmlGetProp(node, (xmlChar *)"uuid");
552     if(!uuid_conf || strcasecmp(uuid_conf, pats[1]))
553       FAIL("internal error uuid");
554
555     /* make sure this isn't a dup */
556     for(a = attr->children; a; a = a->next) {
557       if(!strcmp((char *)a->name, "target"))
558         target = (char *)xmlNodeGetContent(a);
559       if(!strcmp((char *)a->name, "name"))
560         name = (char *)xmlNodeGetContent(a);
561       if(!strcmp((char *)a->name, "module"))
562         module = (char *)xmlNodeGetContent(a);
563     }
564     ocheck = noit_poller_lookup_by_name(target, name);
565     module_change = strcmp(check->module, module);
566     xmlFree(target);
567     xmlFree(name);
568     xmlFree(module);
569     if(ocheck && ocheck != check) FAIL("new target`name would collide");
570     if(module_change) FAIL("cannot change module");
571     configure_xml_check(node, attr, config);
572     parent = make_conf_path(pats[0]);
573     if(!parent) FAIL("invalid path");
574     xmlUnlinkNode(node);
575     xmlAddChild(parent, node);
576   }
577
578   if(noit_conf_write_file(NULL) != 0)
579     noitL(noit_error, "local config write failed\n");
580   noit_conf_mark_changed();
581   noit_poller_reload(xpath);
582   if(restc->call_closure_free) restc->call_closure_free(restc->call_closure);
583   restc->call_closure_free = NULL;
584   restc->call_closure = NULL;
585   if(pobj) xmlXPathFreeObject(pobj);
586   if(doc) xmlFreeDoc(doc);
587   restc->fastpath = rest_show_check;
588   return restc->fastpath(restc, restc->nparams, restc->params);
589
590  error:
591   noit_http_response_standard(ctx, error_code, "ERROR", "text/html");
592   doc = xmlNewDoc((xmlChar *)"1.0");
593   root = xmlNewDocNode(doc, NULL, (xmlChar *)"error", NULL);
594   xmlDocSetRootElement(doc, root);
595   xmlNodeAddContent(root, (xmlChar *)error);
596   noit_http_response_xml(ctx, doc);
597   noit_http_response_end(ctx);
598   goto cleanup;
599
600  cleanup:
601   if(pobj) xmlXPathFreeObject(pobj);
602   if(doc) xmlFreeDoc(doc);
603   return 0;
604 }
605
606 static int
607 rest_show_config(noit_http_rest_closure_t *restc,
608                  int npats, char **pats) {
609   noit_http_session_ctx *ctx = restc->http_ctx;
610   xmlDocPtr doc = NULL;
611   xmlNodePtr node, root;
612   char xpath[1024];
613
614   snprintf(xpath, sizeof(xpath), "/noit%s", pats ? pats[0] : "");
615   node = noit_conf_get_section(NULL, xpath);
616
617   if(!node) {
618     noit_http_response_not_found(ctx, "text/xml");
619     noit_http_response_end(ctx);
620   }
621   else {
622     doc = xmlNewDoc((xmlChar *)"1.0");
623     root = xmlCopyNode(node, 1);
624     xmlDocSetRootElement(doc, root);
625     noit_http_response_ok(ctx, "text/xml");
626     noit_http_response_xml(ctx, doc);
627     noit_http_response_end(ctx);
628   }
629
630   if(doc) xmlFreeDoc(doc);
631
632   return 0;
633 }
634
635 void
636 noit_check_rest_init() {
637   assert(noit_http_rest_register_auth(
638     "GET", "/", "^config(/.*)?$",
639     rest_show_config, noit_http_rest_client_cert_auth
640   ) == 0);
641   assert(noit_http_rest_register_auth(
642     "GET", "/checks/", "^show(/.*)(?<=/)(" UUID_REGEX ")$",
643     rest_show_check, noit_http_rest_client_cert_auth
644   ) == 0);
645   assert(noit_http_rest_register_auth(
646     "PUT", "/checks/", "^set(/.*)(?<=/)(" UUID_REGEX ")$",
647     rest_set_check, noit_http_rest_client_cert_auth
648   ) == 0);
649   assert(noit_http_rest_register_auth(
650     "DELETE", "/checks/", "^delete(/.*)(?<=/)(" UUID_REGEX ")$",
651     rest_delete_check, noit_http_rest_client_cert_auth
652   ) == 0);
653 }
654
Note: See TracBrowser for help on using the browser.