root/src/noit_check_rest.c

Revision a90855c69b0558acdbe4524e80b60e40a40c6b21, 24.4 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 2 years ago)

do some basic validation that the namespaces (prefix) matches what we expect. This isn't XML-ish, but we abuse XML like it wants us to.

  • 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 NS_NODE_CONTENT(parent, ns, k, v, followup) do { \
51   xmlNodePtr tmp; \
52   if(v) { \
53     tmp = xmlNewNode(ns, (xmlChar *)(k)); \
54     xmlNodeAddContent(tmp, (xmlChar *)(v)); \
55     followup \
56     xmlAddChild(parent, tmp); \
57   } \
58 } while(0)
59 #define NODE_CONTENT(parent, k, v) NS_NODE_CONTENT(parent, NULL, k, v, )
60
61 xmlNodePtr
62 noit_check_state_as_xml(noit_check_t *check) {
63   xmlNodePtr state, tmp, metrics;
64   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
65   const char *k;
66   int klen;
67   void *data;
68   stats_t *c = &check->stats.current;
69
70   state = xmlNewNode(NULL, (xmlChar *)"state");
71   NODE_CONTENT(state, "running", NOIT_CHECK_RUNNING(check)?"true":"false");
72   NODE_CONTENT(state, "killed", NOIT_CHECK_KILLED(check)?"true":"false");
73   NODE_CONTENT(state, "configured",
74                NOIT_CHECK_CONFIGURED(check)?"true":"false");
75   NODE_CONTENT(state, "disabled", NOIT_CHECK_DISABLED(check)?"true":"false");
76   NODE_CONTENT(state, "target_ip", check->target_ip);
77   xmlAddChild(state, (tmp = xmlNewNode(NULL, (xmlChar *)"last_run")));
78   if(check->stats.current.whence.tv_sec) {
79     struct timeval f = check->stats.current.whence;
80     struct timeval n;
81     char timestr[20];
82     gettimeofday(&n, NULL);
83     snprintf(timestr, sizeof(timestr), "%0.3f",
84              n.tv_sec + (n.tv_usec / 1000000.0));
85     xmlSetProp(tmp, (xmlChar *)"now", (xmlChar *)timestr);
86     snprintf(timestr, sizeof(timestr), "%0.3f",
87              f.tv_sec + (f.tv_usec / 1000000.0));
88     xmlNodeAddContent(tmp, (xmlChar *)timestr);
89   }
90   if(c->available) { /* truth here means the check has been run */
91     char buff[20], *compiler_warning;
92     snprintf(buff, sizeof(buff), "%0.3f", (float)c->duration/1000.0);
93     compiler_warning = buff;
94     NODE_CONTENT(state, "runtime", compiler_warning);
95   }
96   NODE_CONTENT(state, "availability",
97                noit_check_available_string(c->available));
98   NODE_CONTENT(state, "state", noit_check_state_string(c->state));
99   NODE_CONTENT(state, "status", c->status ? c->status : "");
100   memset(&iter, 0, sizeof(iter));
101   xmlAddChild(state, (metrics = xmlNewNode(NULL, (xmlChar *)"metrics")));
102   while(noit_hash_next(&c->metrics, &iter, &k, &klen, &data)) {
103     char buff[256];
104     metric_t *m = (metric_t *)data;
105     xmlAddChild(metrics, (tmp = xmlNewNode(NULL, (xmlChar *)"metric")));
106     xmlSetProp(tmp, (xmlChar *)"name", (xmlChar *)m->metric_name);
107     buff[0] = m->metric_type; buff[1] = '\0';
108     xmlSetProp(tmp, (xmlChar *)"type", (xmlChar *)buff);
109     if(m->metric_value.s) {
110       int rv;
111       rv = noit_stats_snprint_metric_value(buff, sizeof(buff), m);
112       if(rv < 0)
113         xmlSetProp(tmp, (xmlChar *)"error", (xmlChar *)"unknown type");
114       else
115         xmlNodeAddContent(tmp, (xmlChar *)buff);
116     }
117   }
118   return state;
119 }
120
121 static int
122 rest_show_check(noit_http_rest_closure_t *restc,
123                 int npats, char **pats) {
124   noit_http_session_ctx *ctx = restc->http_ctx;
125   xmlXPathObjectPtr pobj = NULL;
126   xmlXPathContextPtr xpath_ctxt = NULL;
127   xmlDocPtr doc = NULL;
128   xmlNodePtr node, root, attr, config, state, tmp, anode;
129   uuid_t checkid;
130   noit_check_t *check;
131   char xpath[1024], *uuid_conf, *module, *value;
132   int rv, mod, mod_cnt, cnt, error_code = 500;
133   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
134   const char *k;
135   int klen;
136   void *data;
137   noit_hash_table *configh;
138
139   if(npats != 2) goto error;
140
141   rv = noit_check_xpath(xpath, sizeof(xpath), pats[0], pats[1]);
142   if(rv == 0) goto not_found;
143   if(rv < 0) goto error;
144
145   noit_conf_xml_xpath(NULL, &xpath_ctxt);
146   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
147   if(!pobj || pobj->type != XPATH_NODESET ||
148      xmlXPathNodeSetIsEmpty(pobj->nodesetval)) goto not_found;
149   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
150   if(cnt != 1) goto error;
151
152   node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
153   uuid_conf = (char *)xmlGetProp(node, (xmlChar *)"uuid");
154   if(!uuid_conf || uuid_parse(uuid_conf, checkid)) goto error;
155
156   doc = xmlNewDoc((xmlChar *)"1.0");
157   root = xmlNewDocNode(doc, NULL, (xmlChar *)"check", NULL);
158   xmlDocSetRootElement(doc, root);
159
160 #define MYATTR(node,a,n,b) _noit_conf_get_string(node, &(n), "@" #a, &(b))
161 #define INHERIT(node,a,n,b) \
162   _noit_conf_get_string(node, &(n), "ancestor-or-self::node()/@" #a, &(b))
163 #define SHOW_ATTR(parent, node, a) do { \
164   xmlNodePtr anode = NULL; \
165   char *value = NULL; \
166   INHERIT(node, a, anode, value); \
167   if(value != NULL) { \
168     int clen, plen;\
169     const char *cpath, *apath; \
170     xmlNodePtr child; \
171     cpath = node ? (char *)xmlGetNodePath(node) : ""; \
172     apath = anode ? (char *)xmlGetNodePath(anode) : ""; \
173     clen = strlen(cpath); \
174     plen = strlen("/noit/checks"); \
175     child = xmlNewNode(NULL, (xmlChar *)#a); \
176     xmlNodeAddContent(child, (xmlChar *)value); \
177     if(!strncmp(cpath, apath, clen) && apath[clen] == '/') { \
178     } \
179     else { \
180       xmlSetProp(child, (xmlChar *)"inherited", (xmlChar *)apath+plen); \
181     } \
182     xmlAddChild(parent, child); \
183   } \
184 } while(0)
185
186   attr = xmlNewNode(NULL, (xmlChar *)"attributes");
187   xmlAddChild(root, attr);
188
189   SHOW_ATTR(attr,node,uuid);
190
191   /* Name is odd, it falls back transparently to module */
192   if(!INHERIT(node, module, tmp, module)) module = NULL;
193   xmlAddChild(attr, (tmp = xmlNewNode(NULL, (xmlChar *)"name")));
194   if(MYATTR(node, name, anode, value))
195     xmlNodeAddContent(tmp, (xmlChar *)value);
196   else if(module)
197     xmlNodeAddContent(tmp, (xmlChar *)module);
198
199   SHOW_ATTR(attr,node,module);
200   SHOW_ATTR(attr,node,target);
201   SHOW_ATTR(attr,node,resolve_rtype);
202   SHOW_ATTR(attr,node,period);
203   SHOW_ATTR(attr,node,timeout);
204   SHOW_ATTR(attr,node,oncheck);
205   SHOW_ATTR(attr,node,filterset);
206   SHOW_ATTR(attr,node,disable);
207
208   /* Add the config */
209   config = xmlNewNode(NULL, (xmlChar *)"config");
210   configh = noit_conf_get_hash(node, "config");
211   while(noit_hash_next(configh, &iter, &k, &klen, &data))
212     NODE_CONTENT(config, k, data);
213   noit_hash_destroy(configh, free, free);
214   free(configh);
215
216   mod_cnt = noit_check_registered_module_cnt();
217   for(mod=0; mod<mod_cnt; mod++) {
218     noit_conf_section_t toplevel;
219     xmlNodePtr nodeptr;
220     xmlNsPtr ns;
221     const char *nsname;
222     char buff[256];
223
224     nsname = noit_check_registered_module(mod);
225  
226     toplevel = noit_conf_get_section(NULL, "/*");
227     nodeptr = (xmlNodePtr)toplevel;
228     snprintf(buff, sizeof(buff), "noit://module/%s", nsname);
229     ns = xmlSearchNs(nodeptr->doc, nodeptr, (xmlChar *)nsname);
230     if(ns) {
231       configh = noit_conf_get_namespaced_hash(node, "config", nsname);
232       if(configh) {
233         memset(&iter, 0, sizeof(iter));
234         while(noit_hash_next(configh, &iter, &k, &klen, &data)) {
235           NS_NODE_CONTENT(config, ns, "value", data,
236             xmlSetProp(tmp, (xmlChar *)"name", (xmlChar *)k);
237           );
238         }
239         noit_hash_destroy(configh, free, free);
240         free(configh);
241       }
242     }
243   }
244   xmlAddChild(root, config);
245
246   /* Add the state */
247   check = noit_poller_lookup(checkid);
248   if(!check) {
249     state = xmlNewNode(NULL, (xmlChar *)"state");
250     xmlSetProp(state, (xmlChar *)"error", (xmlChar *)"true");
251   }
252   else
253     state = noit_check_state_as_xml(check);
254   xmlAddChild(root, state);
255   noit_http_response_ok(ctx, "text/xml");
256   noit_http_response_xml(ctx, doc);
257   noit_http_response_end(ctx);
258   goto cleanup;
259
260  not_found:
261   noit_http_response_not_found(ctx, "text/html");
262   noit_http_response_end(ctx);
263   goto cleanup;
264
265  error:
266   noit_http_response_standard(ctx, error_code, "ERROR", "text/html");
267   noit_http_response_end(ctx);
268   goto cleanup;
269
270  cleanup:
271   if(pobj) xmlXPathFreeObject(pobj);
272   if(doc) xmlFreeDoc(doc);
273   return 0;
274 }
275
276 static int
277 missing_namespaces(xmlNodePtr ctx, xmlNodePtr q) {
278   xmlNodePtr n;
279   if(q->ns && !xmlSearchNs(ctx->doc, ctx, q->ns->prefix)) return 1;
280   for(n=q->next; n; n=n->next) return missing_namespaces(ctx, n);
281   for(n=q->children; n; n=n->next) return missing_namespaces(ctx, n);
282   return 0;
283 }
284 int
285 noit_validate_check_rest_post(xmlDocPtr doc, xmlNodePtr *a, xmlNodePtr *c,
286                               const char **error) {
287   noit_conf_section_t toplevel;
288   xmlNodePtr root, tl, an, master_config_root;
289   int name=0, module=0, target=0, period=0, timeout=0, filterset=0;
290   *a = *c = NULL;
291   root = xmlDocGetRootElement(doc);
292   /* Make sure any present namespaces are in the master document already */
293   toplevel = noit_conf_get_section(NULL, "/*");
294   master_config_root = (xmlNodePtr)toplevel;
295   if(missing_namespaces(master_config_root, root)) {
296     *error = "invalid namespace provided";
297     return 0;
298   }
299      
300   if(!root || strcmp((char *)root->name, "check")) return 0;
301   for(tl = root->children; tl; tl = tl->next) {
302     if(!strcmp((char *)tl->name, "attributes")) {
303       *a = tl;
304       for(an = tl->children; an; an = an->next) {
305 #define CHECK_N_SET(a) if(!strcmp((char *)an->name, #a))
306         CHECK_N_SET(name) {
307           xmlChar *tmp;
308           pcre *valid_name = noit_conf_get_valid_name_checker();
309           int ovector[30], valid;
310           tmp = xmlNodeGetContent(an);
311           valid = (pcre_exec(valid_name, NULL,
312                              (char *)tmp, strlen((char *)tmp), 0, 0,
313                              ovector, sizeof(ovector)/sizeof(*ovector)) > 0);
314           xmlFree(tmp);
315           if(!valid) { *error = "invalid name"; return 0; }
316           name = 1;
317         }
318         else CHECK_N_SET(module) module = 1; /* This is validated by called */
319         else CHECK_N_SET(target) {
320           noit_boolean should_resolve;
321           int valid;
322           xmlChar *tmp;
323           tmp = xmlNodeGetContent(an);
324           valid = noit_check_is_valid_target((char *)tmp);
325           xmlFree(tmp);
326           if(noit_conf_get_boolean(NULL, "//checks/@resolve_targets",
327                                    &should_resolve) &&
328              should_resolve == noit_false &&
329              !valid) {
330             *error = "invalid target";
331             return 0;
332           }
333           target = 1;
334         }
335         else CHECK_N_SET(resolve_rtype) {
336           xmlChar *tmp;
337           noit_boolean invalid;
338           tmp = xmlNodeGetContent(an);
339           invalid = strcmp((char *)tmp, PREFER_IPV4) &&
340                     strcmp((char *)tmp, PREFER_IPV6) &&
341                     strcmp((char *)tmp, FORCE_IPV4) &&
342                     strcmp((char *)tmp, FORCE_IPV6);
343           xmlFree(tmp);
344           if(invalid) {
345             *error = "invalid reslove_rtype";
346             return 0;
347           }
348         }
349         else CHECK_N_SET(period) {
350           int pint;
351           xmlChar *tmp;
352           tmp = xmlNodeGetContent(an);
353           pint = noit_conf_string_to_int((char *)tmp);
354           xmlFree(tmp);
355           if(pint < 1000 || pint > 300000) {
356             *error = "invalid period";
357             return 0;
358           }
359           period = 1;
360         }
361         else CHECK_N_SET(timeout) {
362           int pint;
363           xmlChar *tmp;
364           tmp = xmlNodeGetContent(an);
365           pint = noit_conf_string_to_int((char *)tmp);
366           xmlFree(tmp);
367           if(pint < 0 || pint > 300000) {
368             *error = "invalid timeout";
369             return 0;
370           }
371           timeout = 1;
372         }
373         else CHECK_N_SET(filterset) {
374           xmlChar *tmp;
375           tmp = xmlNodeGetContent(an);
376           if(!noit_filter_exists((char *)tmp)) {
377             *error = "filterset does not exist";
378             return 0;
379           }
380           filterset = 1;
381         }
382         else CHECK_N_SET(disable) { /* not required */
383           int valid;
384           xmlChar *tmp;
385           tmp = xmlNodeGetContent(an);
386           valid = (!strcasecmp((char *)tmp, "true") ||
387                    !strcasecmp((char *)tmp, "on") ||
388                    !strcasecmp((char *)tmp, "false") ||
389                    !strcasecmp((char *)tmp, "off"));
390           xmlFree(tmp);
391           if(!valid) { *error = "bad disable parameter"; return 0; }
392           target = 1;
393         }
394         else return 0;
395       }
396     }
397     else if(!strcmp((char *)tl->name, "config")) {
398       *c = tl;
399       /* Noop, anything goes */
400     }
401     else return 0;
402   }
403   if(name && module && target && period && timeout && filterset) return 1;
404   *error = "insufficient information";
405   return 0;
406 }
407 static void
408 configure_xml_check(xmlNodePtr parent, xmlNodePtr check, xmlNodePtr a, xmlNodePtr c) {
409   xmlNodePtr n, config, oldconfig;
410   for(n = a->children; n; n = n->next) {
411 #define ATTR2PROP(attr) do { \
412   if(!strcmp((char *)n->name, #attr)) { \
413     xmlChar *v = xmlNodeGetContent(n); \
414     if(v) xmlSetProp(check, n->name, v); \
415     else xmlUnsetProp(check, n->name); \
416     if(v) xmlFree(v); \
417   } \
418 } while(0)
419     ATTR2PROP(name);
420     ATTR2PROP(target);
421     ATTR2PROP(resolve_rtype);
422     ATTR2PROP(module);
423     ATTR2PROP(period);
424     ATTR2PROP(timeout);
425     ATTR2PROP(disable);
426     ATTR2PROP(filterset);
427   }
428   for(oldconfig = check->children; oldconfig; oldconfig = oldconfig->next)
429     if(!strcmp((char *)oldconfig->name, "config")) break;
430   config = xmlNewNode(NULL, (xmlChar *)"config");
431   if(c) {
432     xmlAttrPtr inherit;
433     if((inherit = xmlHasProp(c, (xmlChar *)"inherit")) != NULL &&
434         inherit->children && inherit->children->content)
435       xmlSetProp(config, (xmlChar *)"inherit", inherit->children->content);
436     for(n = c->children; n; n = n->next) {
437       xmlNsPtr targetns = NULL;
438       xmlChar *v = xmlNodeGetContent(n);
439       if(n->ns) {
440         targetns = xmlSearchNs(parent->doc, xmlDocGetRootElement(parent->doc),
441                                n->ns->prefix);
442 noitL(noit_error,"Setting a config value in a namespace (%p)\n", targetns);
443         if(!targetns) targetns = xmlNewNs(config, n->ns->href, n->ns->prefix);
444       }
445       xmlNodePtr co = xmlNewNode(targetns, n->name);
446       if(n->ns && !strcmp((char *)n->name, "value")) {
447         xmlChar *name = xmlGetProp(n, (xmlChar *)"name");
448         if(name) xmlSetProp(co, (xmlChar *)"name", name);
449       }
450       xmlNodeAddContent(co, v);
451       xmlFree(v);
452       xmlAddChild(config, co);
453     }
454   }
455   if(oldconfig) {
456     xmlReplaceNode(oldconfig, config);
457     xmlFreeNode(oldconfig);
458   }
459   else xmlAddChild(check, config);
460   CONF_DIRTY(config);
461 }
462 static xmlNodePtr
463 make_conf_path(char *path) {
464   xmlNodePtr start, tmp;
465   char fullpath[1024], *tok, *brk;
466   if(!path || strlen(path) < 1) return NULL;
467   snprintf(fullpath, sizeof(fullpath), "%s", path+1);
468   fullpath[strlen(fullpath)-1] = '\0';
469   start = noit_conf_get_section(NULL, "/noit/checks");
470   if(!start) return NULL;
471   for (tok = strtok_r(fullpath, "/", &brk);
472        tok;
473        tok = strtok_r(NULL, "/", &brk)) {
474     if(!xmlValidateNameValue((xmlChar *)tok)) return NULL;
475     if(!strcmp(tok, "check")) return NULL;  /* These two paths */
476     if(!strcmp(tok, "config")) return NULL; /* are off limits. */
477     for (tmp = start->children; tmp; tmp = tmp->next) {
478       if(!strcmp((char *)tmp->name, tok)) break;
479     }
480     if(!tmp) {
481       tmp = xmlNewNode(NULL, (xmlChar *)tok);
482       xmlAddChild(start, tmp);
483       CONF_DIRTY(tmp);
484     }
485     start = tmp;
486   }
487   return start;
488 }
489 static int
490 rest_delete_check(noit_http_rest_closure_t *restc,
491                   int npats, char **pats) {
492   noit_http_session_ctx *ctx = restc->http_ctx;
493   xmlXPathObjectPtr pobj = NULL;
494   xmlXPathContextPtr xpath_ctxt = NULL;
495   xmlNodePtr node;
496   uuid_t checkid;
497   noit_check_t *check;
498   const char *error;
499   char xpath[1024], *uuid_conf;
500   int rv, cnt, error_code = 500;
501   noit_boolean exists = noit_false;
502
503   if(npats != 2) goto error;
504
505   if(uuid_parse(pats[1], checkid)) goto error;
506   check = noit_poller_lookup(checkid);
507   if(check)
508     exists = noit_true;
509
510   rv = noit_check_xpath(xpath, sizeof(xpath), pats[0], pats[1]);
511   if(rv == 0) FAIL("uuid not valid");
512   if(rv < 0) FAIL("Tricky McTrickster... No");
513
514   noit_conf_xml_xpath(NULL, &xpath_ctxt);
515   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
516   if(!pobj || pobj->type != XPATH_NODESET ||
517      xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
518     if(exists) { error_code = 403; FAIL("uuid not yours"); }
519     goto not_found;
520   }
521   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
522   if(cnt != 1) FAIL("internal error, |checkid| > 1");
523   node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
524   uuid_conf = (char *)xmlGetProp(node, (xmlChar *)"uuid");
525   if(!uuid_conf || strcasecmp(uuid_conf, pats[1]))
526     FAIL("internal error uuid");
527
528   /* delete this here */
529   noit_poller_deschedule(check->checkid);
530   CONF_REMOVE(node);
531   xmlUnlinkNode(node);
532   xmlFreeNode(node);
533   noit_conf_mark_changed();
534   if(noit_conf_write_file(NULL) != 0)
535     noitL(noit_error, "local config write failed\n");
536   noit_http_response_ok(ctx, "text/html");
537   noit_http_response_end(ctx);
538   goto cleanup;
539
540  not_found:
541   noit_http_response_not_found(ctx, "text/html");
542   noit_http_response_end(ctx);
543   goto cleanup;
544
545  error:
546   noit_http_response_standard(ctx, error_code, "ERROR", "text/html");
547   noit_http_response_end(ctx);
548   goto cleanup;
549
550  cleanup:
551   if(pobj) xmlXPathFreeObject(pobj);
552   (void)error;
553   return 0;
554 }
555
556 static int
557 rest_set_check(noit_http_rest_closure_t *restc,
558                int npats, char **pats) {
559   noit_http_session_ctx *ctx = restc->http_ctx;
560   xmlXPathObjectPtr pobj = NULL;
561   xmlXPathContextPtr xpath_ctxt = NULL;
562   xmlDocPtr doc = NULL, indoc = NULL;
563   xmlNodePtr node, root, attr, config, parent;
564   uuid_t checkid;
565   noit_check_t *check;
566   char xpath[1024], *uuid_conf;
567   int rv, cnt, error_code = 500, complete = 0, mask = 0;
568   const char *error = "internal error";
569   noit_boolean exists = noit_false;
570
571   if(npats != 2) goto error;
572
573   indoc = rest_get_xml_upload(restc, &mask, &complete);
574   if(!complete) return mask;
575   if(indoc == NULL) FAIL("xml parse error");
576   if(!noit_validate_check_rest_post(indoc, &attr, &config, &error)) goto error;
577
578   if(uuid_parse(pats[1], checkid)) goto error;
579   check = noit_poller_lookup(checkid);
580   if(check)
581     exists = noit_true;
582
583   rv = noit_check_xpath(xpath, sizeof(xpath), pats[0], pats[1]);
584   if(rv == 0) FAIL("uuid not valid");
585   if(rv < 0) FAIL("Tricky McTrickster... No");
586
587   noit_conf_xml_xpath(NULL, &xpath_ctxt);
588   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
589   if(!pobj || pobj->type != XPATH_NODESET ||
590      xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
591     if(exists) { error_code = 403; FAIL("uuid not yours"); }
592     else {
593       char *target = NULL, *name = NULL, *module = NULL;
594       noit_module_t *m;
595       xmlNodePtr newcheck, a;
596       /* make sure this isn't a dup */
597       for(a = attr->children; a; a = a->next) {
598         if(!strcmp((char *)a->name, "target"))
599           target = (char *)xmlNodeGetContent(a);
600         if(!strcmp((char *)a->name, "name"))
601           name = (char *)xmlNodeGetContent(a);
602         if(!strcmp((char *)a->name, "module"))
603           module = (char *)xmlNodeGetContent(a);
604       }
605       exists = (noit_poller_lookup_by_name(target, name) != NULL);
606       m = noit_module_lookup(module);
607       xmlFree(target);
608       xmlFree(name);
609       xmlFree(module);
610       if(exists) FAIL("target`name already registered");
611       if(!m) FAIL("module does not exist");
612       /* create a check here */
613       newcheck = xmlNewNode(NULL, (xmlChar *)"check");
614       xmlSetProp(newcheck, (xmlChar *)"uuid", (xmlChar *)pats[1]);
615       parent = make_conf_path(pats[0]);
616       if(!parent) FAIL("invalid path");
617       configure_xml_check(parent, newcheck, attr, config);
618       xmlAddChild(parent, newcheck);
619       CONF_DIRTY(newcheck);
620     }
621   }
622   if(exists) {
623     int module_change;
624     char *target = NULL, *name = NULL, *module = NULL;
625     xmlNodePtr a;
626     noit_check_t *ocheck;
627
628     cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
629     if(cnt != 1) FAIL("internal error, |checkid| > 1");
630     node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
631     uuid_conf = (char *)xmlGetProp(node, (xmlChar *)"uuid");
632     if(!uuid_conf || strcasecmp(uuid_conf, pats[1]))
633       FAIL("internal error uuid");
634
635     /* make sure this isn't a dup */
636     for(a = attr->children; a; a = a->next) {
637       if(!strcmp((char *)a->name, "target"))
638         target = (char *)xmlNodeGetContent(a);
639       if(!strcmp((char *)a->name, "name"))
640         name = (char *)xmlNodeGetContent(a);
641       if(!strcmp((char *)a->name, "module"))
642         module = (char *)xmlNodeGetContent(a);
643     }
644     ocheck = noit_poller_lookup_by_name(target, name);
645     module_change = strcmp(check->module, module);
646     xmlFree(target);
647     xmlFree(name);
648     xmlFree(module);
649     if(ocheck && ocheck != check) FAIL("new target`name would collide");
650     if(module_change) FAIL("cannot change module");
651     parent = make_conf_path(pats[0]);
652     if(!parent) FAIL("invalid path");
653     configure_xml_check(parent, node, attr, config);
654     xmlUnlinkNode(node);
655     xmlAddChild(parent, node);
656     CONF_DIRTY(node);
657   }
658
659   noit_conf_mark_changed();
660   if(noit_conf_write_file(NULL) != 0)
661     noitL(noit_error, "local config write failed\n");
662   noit_poller_reload(xpath);
663   if(restc->call_closure_free) restc->call_closure_free(restc->call_closure);
664   restc->call_closure_free = NULL;
665   restc->call_closure = NULL;
666   if(pobj) xmlXPathFreeObject(pobj);
667   if(doc) xmlFreeDoc(doc);
668   restc->fastpath = rest_show_check;
669   return restc->fastpath(restc, restc->nparams, restc->params);
670
671  error:
672   noit_http_response_standard(ctx, error_code, "ERROR", "text/html");
673   doc = xmlNewDoc((xmlChar *)"1.0");
674   root = xmlNewDocNode(doc, NULL, (xmlChar *)"error", NULL);
675   xmlDocSetRootElement(doc, root);
676   xmlNodeAddContent(root, (xmlChar *)error);
677   noit_http_response_xml(ctx, doc);
678   noit_http_response_end(ctx);
679   goto cleanup;
680
681  cleanup:
682   if(pobj) xmlXPathFreeObject(pobj);
683   if(doc) xmlFreeDoc(doc);
684   return 0;
685 }
686
687 static int
688 rest_show_config(noit_http_rest_closure_t *restc,
689                  int npats, char **pats) {
690   noit_http_session_ctx *ctx = restc->http_ctx;
691   xmlDocPtr doc = NULL;
692   xmlNodePtr node, root;
693   char xpath[1024];
694
695   snprintf(xpath, sizeof(xpath), "/noit%s", pats ? pats[0] : "");
696   node = noit_conf_get_section(NULL, xpath);
697
698   if(!node) {
699     noit_http_response_not_found(ctx, "text/xml");
700     noit_http_response_end(ctx);
701   }
702   else {
703     doc = xmlNewDoc((xmlChar *)"1.0");
704     root = xmlCopyNode(node, 1);
705     xmlDocSetRootElement(doc, root);
706     noit_http_response_ok(ctx, "text/xml");
707     noit_http_response_xml(ctx, doc);
708     noit_http_response_end(ctx);
709   }
710
711   if(doc) xmlFreeDoc(doc);
712
713   return 0;
714 }
715
716 void
717 noit_check_rest_init() {
718   assert(noit_http_rest_register_auth(
719     "GET", "/", "^config(/.*)?$",
720     rest_show_config, noit_http_rest_client_cert_auth
721   ) == 0);
722   assert(noit_http_rest_register_auth(
723     "GET", "/checks/", "^show(/.*)(?<=/)(" UUID_REGEX ")$",
724     rest_show_check, noit_http_rest_client_cert_auth
725   ) == 0);
726   assert(noit_http_rest_register_auth(
727     "PUT", "/checks/", "^set(/.*)(?<=/)(" UUID_REGEX ")$",
728     rest_set_check, noit_http_rest_client_cert_auth
729   ) == 0);
730   assert(noit_http_rest_register_auth(
731     "DELETE", "/checks/", "^delete(/.*)(?<=/)(" UUID_REGEX ")$",
732     rest_delete_check, noit_http_rest_client_cert_auth
733   ) == 0);
734 }
735
Note: See TracBrowser for help on using the browser.