root/src/noit_check_rest.c

Revision 3eca56e24a3dacaef159473562f669d9d546035e, 32.6 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 4 months ago)

Address a particularly hot startup path...

61% of check load time is spent doing:
noit_conf_get_boolean(NULL, "//checks/@resolve_targets", ...)

This caches that answer.

  • 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 #include "json-lib/json.h"
48
49 #define FAIL(a) do { error = (a); goto error; } while(0)
50
51 #define NS_NODE_CONTENT(parent, ns, k, v, followup) do { \
52   xmlNodePtr tmp; \
53   if(v) { \
54     tmp = xmlNewNode(ns, (xmlChar *)(k)); \
55     xmlNodeAddContent(tmp, (xmlChar *)(v)); \
56     followup \
57     xmlAddChild(parent, tmp); \
58   } \
59 } while(0)
60 #define NODE_CONTENT(parent, k, v) NS_NODE_CONTENT(parent, NULL, k, v, )
61
62 static int
63 rest_show_config(noit_http_rest_closure_t *, int, char **);
64
65 static void
66 add_metrics_to_node(stats_t *c, xmlNodePtr metrics, const char *type,
67                     int include_time) {
68   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
69   const char *k;
70   int klen;
71   void *data;
72   xmlNodePtr tmp;
73   while(noit_hash_next(&c->metrics, &iter, &k, &klen, &data)) {
74     char buff[256];
75     metric_t *m = (metric_t *)data;
76     xmlAddChild(metrics, (tmp = xmlNewNode(NULL, (xmlChar *)"metric")));
77     xmlSetProp(tmp, (xmlChar *)"name", (xmlChar *)m->metric_name);
78     buff[0] = m->metric_type; buff[1] = '\0';
79     xmlSetProp(tmp, (xmlChar *)"type", (xmlChar *)buff);
80     if(m->metric_value.s) {
81       int rv;
82       rv = noit_stats_snprint_metric_value(buff, sizeof(buff), m);
83       if(rv < 0)
84         xmlSetProp(tmp, (xmlChar *)"error", (xmlChar *)"unknown type");
85       else
86         xmlNodeAddContent(tmp, (xmlChar *)buff);
87     }
88   }
89   xmlSetProp(metrics, (xmlChar *)"type", (const xmlChar *) type);
90   if(include_time) {
91     struct timeval f = c->whence;
92     char timestr[20];
93     snprintf(timestr, sizeof(timestr), "%0.3f",
94              f.tv_sec + (f.tv_usec / 1000000.0));
95     xmlSetProp(metrics, (xmlChar *)"timestamp", (xmlChar *)timestr);
96   }
97 }
98 xmlNodePtr
99 noit_check_state_as_xml(noit_check_t *check) {
100   xmlNodePtr state, tmp, metrics;
101   struct timeval now;
102   stats_t *c = &check->stats.current;
103
104   gettimeofday(&now, NULL);
105   state = xmlNewNode(NULL, (xmlChar *)"state");
106   NODE_CONTENT(state, "running", NOIT_CHECK_RUNNING(check)?"true":"false");
107   NODE_CONTENT(state, "killed", NOIT_CHECK_KILLED(check)?"true":"false");
108   NODE_CONTENT(state, "configured",
109                NOIT_CHECK_CONFIGURED(check)?"true":"false");
110   NODE_CONTENT(state, "disabled", NOIT_CHECK_DISABLED(check)?"true":"false");
111   NODE_CONTENT(state, "target_ip", check->target_ip);
112   xmlAddChild(state, (tmp = xmlNewNode(NULL, (xmlChar *)"last_run")));
113   if(check->stats.current.whence.tv_sec) {
114     struct timeval f = check->stats.current.whence;
115     char timestr[20];
116     snprintf(timestr, sizeof(timestr), "%0.3f",
117              now.tv_sec + (now.tv_usec / 1000000.0));
118     xmlSetProp(tmp, (xmlChar *)"now", (xmlChar *)timestr);
119     snprintf(timestr, sizeof(timestr), "%0.3f",
120              f.tv_sec + (f.tv_usec / 1000000.0));
121     xmlNodeAddContent(tmp, (xmlChar *)timestr);
122   }
123   if(c->available) { /* truth here means the check has been run */
124     char buff[20], *compiler_warning;
125     snprintf(buff, sizeof(buff), "%0.3f", (float)c->duration/1000.0);
126     compiler_warning = buff;
127     NODE_CONTENT(state, "runtime", compiler_warning);
128   }
129   NODE_CONTENT(state, "availability",
130                noit_check_available_string(c->available));
131   NODE_CONTENT(state, "state", noit_check_state_string(c->state));
132   NODE_CONTENT(state, "status", c->status ? c->status : "");
133   xmlAddChild(state, (metrics = xmlNewNode(NULL, (xmlChar *)"metrics")));
134   add_metrics_to_node(&check->stats.inprogress, metrics, "inprogress", 0);
135   if(check->stats.current.whence.tv_sec) {
136     xmlAddChild(state, (metrics = xmlNewNode(NULL, (xmlChar *)"metrics")));
137     add_metrics_to_node(&check->stats.current, metrics, "current", 1);
138   }
139   if(check->stats.previous.whence.tv_sec) {
140     xmlAddChild(state, (metrics = xmlNewNode(NULL, (xmlChar *)"metrics")));
141     add_metrics_to_node(&check->stats.previous, metrics, "previous", 1);
142   }
143   return state;
144 }
145
146 static struct json_object *
147 stats_to_json(stats_t *c) {
148   struct json_object *doc;
149   doc = json_object_new_object();
150   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
151   const char *k;
152   int klen;
153   void *data;
154   while(noit_hash_next(&c->metrics, &iter, &k, &klen, &data)) {
155     char buff[256];
156     metric_t *m = (metric_t *)data;
157     struct json_object *metric = json_object_new_object();
158     buff[0] = m->metric_type; buff[1] = '\0';
159     json_object_object_add(metric, "_type", json_object_new_string(buff));
160     if(m->metric_value.s) {
161       int rv;
162       rv = noit_stats_snprint_metric_value(buff, sizeof(buff), m);
163       if(rv >= 0)
164         json_object_object_add(metric, "_value", json_object_new_string(buff));
165     }
166     json_object_object_add(doc, m->metric_name, metric);
167   }
168   return doc;
169 }
170
171 static struct json_object *
172 noit_check_to_json(noit_check_t *check, int full) {
173   char id_str[UUID_STR_LEN+1];
174   struct json_object *j_last_run, *j_next_run;
175   struct timeval *t;
176   u_int64_t ms = 0;
177   struct json_object *doc;
178   uuid_unparse_lower(check->checkid, id_str);
179
180   doc = json_object_new_object();
181   json_object_object_add(doc, "id", json_object_new_string(id_str));
182   json_object_object_add(doc, "name", json_object_new_string(check->name));
183   json_object_object_add(doc, "module", json_object_new_string(check->module));
184   json_object_object_add(doc, "target", json_object_new_string(check->target));
185   json_object_object_add(doc, "target_ip", json_object_new_string(check->target_ip));
186   json_object_object_add(doc, "filterset", json_object_new_string(check->filterset));
187   json_object_object_add(doc, "period", json_object_new_int(check->period));
188   json_object_object_add(doc, "timeout", json_object_new_int(check->timeout));
189   json_object_object_add(doc, "flags", json_object_new_int(check->flags));
190
191   t = &check->stats.current.whence;
192   j_last_run = json_object_new_int(ms);
193   json_object_set_int_overflow(j_last_run, json_overflow_uint64);
194   ms = t->tv_sec;
195   ms *= 1000ULL;
196   ms += t->tv_usec/1000;
197   json_object_set_uint64(j_last_run, ms);
198   json_object_object_add(doc, "last_run", j_last_run);
199
200   t = check->fire_event ? &check->fire_event->whence : NULL;
201   if(t) {
202     j_next_run = json_object_new_int(ms);
203     json_object_set_int_overflow(j_next_run, json_overflow_uint64);
204     ms = t->tv_sec;
205     ms *= 1000ULL;
206     ms += t->tv_usec/1000;
207     json_object_set_uint64(j_next_run, ms);
208     json_object_object_add(doc, "next_run", j_next_run);
209   }
210
211   if(full) {
212     noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
213     const char *k;
214     int klen;
215     void *data;
216     noit_hash_table *configh;
217     struct timeval f;
218     char timestr[20];
219     struct json_object *status, *metrics, *config;
220
221     /* config */
222     config = json_object_new_object();
223     configh = check->config;
224     while(noit_hash_next(configh, &iter, &k, &klen, &data))
225       json_object_object_add(config, k, json_object_new_string(data));
226     json_object_object_add(doc, "config", config);
227
228     /* status */
229     status = json_object_new_object();
230     switch(check->stats.current.available) {
231       case NP_UNKNOWN: break;
232       case NP_AVAILABLE:
233         json_object_object_add(status, "available", json_object_new_boolean(1));
234         break;
235       case NP_UNAVAILABLE:
236         json_object_object_add(status, "available", json_object_new_boolean(0));
237         break;
238     }
239     switch(check->stats.current.state) {
240       case NP_UNKNOWN: break;
241       case NP_GOOD:
242         json_object_object_add(status, "good", json_object_new_boolean(1));
243         break;
244       case NP_BAD:
245         json_object_object_add(status, "good", json_object_new_boolean(0));
246         break;
247     }
248     json_object_object_add(doc, "status", status);
249     metrics = json_object_new_object();
250
251     f = check->stats.current.whence;
252     if(f.tv_sec) {
253       json_object_object_add(metrics, "current", stats_to_json(&check->stats.current));
254       snprintf(timestr, sizeof(timestr), "%llu%03d",
255                (unsigned long long int)f.tv_sec, (f.tv_usec / 1000));
256       json_object_object_add(metrics, "current_timestamp", json_object_new_string(timestr));
257     }
258
259     f = check->stats.inprogress.whence;
260     if(f.tv_sec) {
261       json_object_object_add(metrics, "inprogress", stats_to_json(&check->stats.inprogress));
262       snprintf(timestr, sizeof(timestr), "%llu%03d",
263                (unsigned long long int)f.tv_sec, (f.tv_usec / 1000));
264       json_object_object_add(metrics, "inprogress_timestamp", json_object_new_string(timestr));
265     }
266
267     f = check->stats.previous.whence;
268     if(f.tv_sec) {
269       json_object_object_add(metrics, "previous", stats_to_json(&check->stats.previous));
270       snprintf(timestr, sizeof(timestr), "%llu%03d",
271                (unsigned long long int)f.tv_sec, (f.tv_usec / 1000));
272       json_object_object_add(metrics, "previous_timestamp", json_object_new_string(timestr));
273     }
274
275     json_object_object_add(doc, "metrics", metrics);
276   }
277   return doc;
278 }
279
280 static int
281 json_check_accum(noit_check_t *check, void *closure) {
282   struct json_object *cobj, *doc = closure;
283   char id_str[UUID_STR_LEN+1];
284   uuid_unparse_lower(check->checkid, id_str);
285   cobj = noit_check_to_json(check, 0);
286   json_object_object_del(cobj, "id");
287   json_object_object_add(doc, id_str, cobj);
288   return 1;
289 }
290 static int
291 rest_show_checks_json(noit_http_rest_closure_t *restc,
292                       int npats, char **pats) {
293   const char *jsonstr;
294   struct json_object *doc;
295   doc = json_object_new_object();
296   noit_poller_do(json_check_accum, doc);
297
298   noit_http_response_ok(restc->http_ctx, "application/json");
299   jsonstr = json_object_to_json_string(doc);
300   noit_http_response_append(restc->http_ctx, jsonstr, strlen(jsonstr));
301   noit_http_response_append(restc->http_ctx, "\n", 1);
302   json_object_put(doc);
303   noit_http_response_end(restc->http_ctx);
304   return 0;
305 }
306 static int
307 rest_show_checks(noit_http_rest_closure_t *restc,
308                  int npats, char **pats) {
309   char *cpath = "/checks";
310
311   if(npats == 1 && !strcmp(pats[0], ".json"))
312     return rest_show_checks_json(restc, npats, pats);
313
314   return rest_show_config(restc, 1, &cpath);
315 }
316
317 static int
318 rest_show_check_json(noit_http_rest_closure_t *restc,
319                      uuid_t checkid) {
320   noit_check_t *check;
321   struct json_object *doc;
322   const char *jsonstr;
323   check = noit_poller_lookup(checkid);
324   if(!check) {
325     noit_http_response_not_found(restc->http_ctx, "application/json");
326     noit_http_response_end(restc->http_ctx);
327     return 0;
328   }
329
330   doc = noit_check_to_json(check, 1);
331  
332   noit_http_response_ok(restc->http_ctx, "application/json");
333   jsonstr = json_object_to_json_string(doc);
334   noit_http_response_append(restc->http_ctx, jsonstr, strlen(jsonstr));
335   noit_http_response_append(restc->http_ctx, "\n", 1);
336   json_object_put(doc);
337   noit_http_response_end(restc->http_ctx);
338   return 0;
339 }
340 static int
341 rest_show_check(noit_http_rest_closure_t *restc,
342                 int npats, char **pats) {
343   noit_http_session_ctx *ctx = restc->http_ctx;
344   xmlXPathObjectPtr pobj = NULL;
345   xmlXPathContextPtr xpath_ctxt = NULL;
346   xmlDocPtr doc = NULL;
347   xmlNodePtr node, root, attr, config, state, tmp, anode;
348   uuid_t checkid;
349   noit_check_t *check;
350   char xpath[1024], *uuid_conf, *module = NULL, *value = NULL;
351   int rv, mod, mod_cnt, cnt, error_code = 500;
352   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
353   const char *k;
354   int klen;
355   void *data;
356   noit_hash_table *configh;
357
358   if(npats != 2 && npats != 3) goto error;
359
360   rv = noit_check_xpath(xpath, sizeof(xpath), pats[0], pats[1]);
361   if(rv == 0) goto not_found;
362   if(rv < 0) goto error;
363
364   noit_conf_xml_xpath(NULL, &xpath_ctxt);
365   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
366   if(!pobj || pobj->type != XPATH_NODESET ||
367      xmlXPathNodeSetIsEmpty(pobj->nodesetval)) goto not_found;
368   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
369   if(cnt != 1) goto error;
370
371   node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
372   uuid_conf = (char *)xmlGetProp(node, (xmlChar *)"uuid");
373   if(!uuid_conf || uuid_parse(uuid_conf, checkid)) goto error;
374
375   if(npats == 3 && !strcmp(pats[2], ".json")) {
376     return rest_show_check_json(restc, checkid);
377   }
378
379   doc = xmlNewDoc((xmlChar *)"1.0");
380   root = xmlNewDocNode(doc, NULL, (xmlChar *)"check", NULL);
381   xmlDocSetRootElement(doc, root);
382
383 #define MYATTR(node,a,n,b) _noit_conf_get_string(node, &(n), "@" #a, &(b))
384 #define INHERIT(node,a,n,b) \
385   _noit_conf_get_string(node, &(n), "ancestor-or-self::node()/@" #a, &(b))
386 #define SHOW_ATTR(parent, node, a) do { \
387   char *_value = NULL; \
388   INHERIT(node, a, anode, _value); \
389   if(_value != NULL) { \
390     int clen, plen;\
391     char *_cpath, *_apath; \
392     xmlNodePtr child; \
393     _cpath = node ? (char *)xmlGetNodePath(node) : strdup(""); \
394     _apath = anode ? (char *)xmlGetNodePath(anode) : strdup(""); \
395     clen = strlen(_cpath); \
396     plen = strlen("/noit/checks"); \
397     child = xmlNewNode(NULL, (xmlChar *)#a); \
398     xmlNodeAddContent(child, (xmlChar *)_value); \
399     if(!strncmp(_cpath, _apath, clen) && _apath[clen] == '/') { \
400     } \
401     else { \
402       xmlSetProp(child, (xmlChar *)"inherited", (xmlChar *)_apath+plen); \
403     } \
404     xmlAddChild(parent, child); \
405     free(_cpath); \
406     free(_apath); \
407     free(_value); \
408   } \
409 } while(0)
410
411   attr = xmlNewNode(NULL, (xmlChar *)"attributes");
412   xmlAddChild(root, attr);
413
414   SHOW_ATTR(attr,node,uuid);
415
416   /* Name is odd, it falls back transparently to module */
417   if(!INHERIT(node, module, tmp, module)) module = NULL;
418   xmlAddChild(attr, (tmp = xmlNewNode(NULL, (xmlChar *)"name")));
419   if(MYATTR(node, name, anode, value))
420     xmlNodeAddContent(tmp, (xmlChar *)value);
421   else if(module)
422     xmlNodeAddContent(tmp, (xmlChar *)module);
423   if(value) free(value);
424   if(module) free(module);
425
426   SHOW_ATTR(attr,node,module);
427   SHOW_ATTR(attr,node,target);
428   SHOW_ATTR(attr,node,resolve_rtype);
429   SHOW_ATTR(attr,node,period);
430   SHOW_ATTR(attr,node,timeout);
431   SHOW_ATTR(attr,node,oncheck);
432   SHOW_ATTR(attr,node,filterset);
433   SHOW_ATTR(attr,node,disable);
434
435   /* Add the config */
436   config = xmlNewNode(NULL, (xmlChar *)"config");
437   configh = noit_conf_get_hash(node, "config");
438   while(noit_hash_next(configh, &iter, &k, &klen, &data))
439     NODE_CONTENT(config, k, data);
440   noit_hash_destroy(configh, free, free);
441   free(configh);
442
443   mod_cnt = noit_check_registered_module_cnt();
444   for(mod=0; mod<mod_cnt; mod++) {
445     xmlNsPtr ns;
446     const char *nsname;
447     char buff[256];
448
449     nsname = noit_check_registered_module(mod);
450  
451     snprintf(buff, sizeof(buff), "noit://module/%s", nsname);
452     ns = xmlSearchNs(root->doc, root, (xmlChar *)nsname);
453     if(!ns) ns = xmlNewNs(root, (xmlChar *)buff, (xmlChar *)nsname);
454     if(ns) {
455       configh = noit_conf_get_namespaced_hash(node, "config", nsname);
456       if(configh) {
457         memset(&iter, 0, sizeof(iter));
458         while(noit_hash_next(configh, &iter, &k, &klen, &data)) {
459           NS_NODE_CONTENT(config, ns, "value", data,
460             xmlSetProp(tmp, (xmlChar *)"name", (xmlChar *)k);
461           );
462         }
463         noit_hash_destroy(configh, free, free);
464         free(configh);
465       }
466     }
467   }
468   xmlAddChild(root, config);
469
470   /* Add the state */
471   check = noit_poller_lookup(checkid);
472   if(!check) {
473     state = xmlNewNode(NULL, (xmlChar *)"state");
474     xmlSetProp(state, (xmlChar *)"error", (xmlChar *)"true");
475   }
476   else
477     state = noit_check_state_as_xml(check);
478   xmlAddChild(root, state);
479   noit_http_response_ok(ctx, "text/xml");
480   noit_http_response_xml(ctx, doc);
481   noit_http_response_end(ctx);
482   goto cleanup;
483
484  not_found:
485   noit_http_response_not_found(ctx, "text/html");
486   noit_http_response_end(ctx);
487   goto cleanup;
488
489  error:
490   noit_http_response_standard(ctx, error_code, "ERROR", "text/html");
491   noit_http_response_end(ctx);
492   goto cleanup;
493
494  cleanup:
495   if(pobj) xmlXPathFreeObject(pobj);
496   if(doc) xmlFreeDoc(doc);
497   return 0;
498 }
499
500 static int
501 missing_namespaces(xmlNodePtr ctx, xmlNodePtr q) {
502   xmlNodePtr n;
503   if(q->ns && !xmlSearchNs(ctx->doc, ctx, q->ns->prefix)) return 1;
504   for(n=q->next; n; n=n->next) return missing_namespaces(ctx, n);
505   for(n=q->children; n; n=n->next) return missing_namespaces(ctx, n);
506   return 0;
507 }
508 int
509 noit_validate_check_rest_post(xmlDocPtr doc, xmlNodePtr *a, xmlNodePtr *c,
510                               const char **error) {
511   noit_conf_section_t toplevel;
512   xmlNodePtr root, tl, an, master_config_root;
513   int name=0, module=0, target=0, period=0, timeout=0, filterset=0;
514   *a = *c = NULL;
515   root = xmlDocGetRootElement(doc);
516   /* Make sure any present namespaces are in the master document already */
517   toplevel = noit_conf_get_section(NULL, "/*");
518   master_config_root = (xmlNodePtr)toplevel;
519   if(!master_config_root) {
520     *error = "no document";
521     return 0;
522   }
523   if(missing_namespaces(master_config_root, root)) {
524     *error = "invalid namespace provided";
525     return 0;
526   }
527      
528   if(strcmp((char *)root->name, "check")) return 0;
529   for(tl = root->children; tl; tl = tl->next) {
530     if(!strcmp((char *)tl->name, "attributes")) {
531       *a = tl;
532       for(an = tl->children; an; an = an->next) {
533 #define CHECK_N_SET(a) if(!strcmp((char *)an->name, #a))
534         CHECK_N_SET(name) {
535           xmlChar *tmp;
536           pcre *valid_name = noit_conf_get_valid_name_checker();
537           int ovector[30], valid;
538           tmp = xmlNodeGetContent(an);
539           valid = (pcre_exec(valid_name, NULL,
540                              (char *)tmp, strlen((char *)tmp), 0, 0,
541                              ovector, sizeof(ovector)/sizeof(*ovector)) > 0);
542           xmlFree(tmp);
543           if(!valid) { *error = "invalid name"; return 0; }
544           name = 1;
545         }
546         else CHECK_N_SET(module) module = 1; /* This is validated by called */
547         else CHECK_N_SET(target) {
548           noit_boolean should_resolve;
549           int valid;
550           xmlChar *tmp;
551           tmp = xmlNodeGetContent(an);
552           valid = noit_check_is_valid_target((char *)tmp);
553           xmlFree(tmp);
554           if(noit_conf_should_resolve_targets(&should_resolve) &&
555              should_resolve == noit_false &&
556              !valid) {
557             *error = "invalid target";
558             return 0;
559           }
560           target = 1;
561         }
562         else CHECK_N_SET(resolve_rtype) {
563           xmlChar *tmp;
564           noit_boolean invalid;
565           tmp = xmlNodeGetContent(an);
566           invalid = strcmp((char *)tmp, PREFER_IPV4) &&
567                     strcmp((char *)tmp, PREFER_IPV6) &&
568                     strcmp((char *)tmp, FORCE_IPV4) &&
569                     strcmp((char *)tmp, FORCE_IPV6);
570           xmlFree(tmp);
571           if(invalid) {
572             *error = "invalid reslove_rtype";
573             return 0;
574           }
575         }
576         else CHECK_N_SET(period) {
577           int pint;
578           xmlChar *tmp;
579           tmp = xmlNodeGetContent(an);
580           pint = noit_conf_string_to_int((char *)tmp);
581           xmlFree(tmp);
582           if(pint < 1000 || pint > 300000) {
583             *error = "invalid period";
584             return 0;
585           }
586           period = 1;
587         }
588         else CHECK_N_SET(timeout) {
589           int pint;
590           xmlChar *tmp;
591           tmp = xmlNodeGetContent(an);
592           pint = noit_conf_string_to_int((char *)tmp);
593           xmlFree(tmp);
594           if(pint < 0 || pint > 300000) {
595             *error = "invalid timeout";
596             return 0;
597           }
598           timeout = 1;
599         }
600         else CHECK_N_SET(filterset) {
601           xmlChar *tmp;
602           tmp = xmlNodeGetContent(an);
603           if(!noit_filter_exists((char *)tmp)) {
604             *error = "filterset does not exist";
605             return 0;
606           }
607           filterset = 1;
608         }
609         else CHECK_N_SET(disable) { /* not required */
610           int valid;
611           xmlChar *tmp;
612           tmp = xmlNodeGetContent(an);
613           valid = (!strcasecmp((char *)tmp, "true") ||
614                    !strcasecmp((char *)tmp, "on") ||
615                    !strcasecmp((char *)tmp, "false") ||
616                    !strcasecmp((char *)tmp, "off"));
617           xmlFree(tmp);
618           if(!valid) { *error = "bad disable parameter"; return 0; }
619           target = 1;
620         }
621         else return 0;
622       }
623     }
624     else if(!strcmp((char *)tl->name, "config")) {
625       *c = tl;
626       /* Noop, anything goes */
627     }
628     else return 0;
629   }
630   if(name && module && target && period && timeout && filterset) return 1;
631   *error = "insufficient information";
632   return 0;
633 }
634 static void
635 configure_xml_check(xmlNodePtr parent, xmlNodePtr check, xmlNodePtr a, xmlNodePtr c) {
636   xmlNodePtr n, config, oldconfig;
637   for(n = a->children; n; n = n->next) {
638 #define ATTR2PROP(attr) do { \
639   if(!strcmp((char *)n->name, #attr)) { \
640     xmlChar *v = xmlNodeGetContent(n); \
641     if(v) xmlSetProp(check, n->name, v); \
642     else xmlUnsetProp(check, n->name); \
643     if(v) xmlFree(v); \
644   } \
645 } while(0)
646     ATTR2PROP(name);
647     ATTR2PROP(target);
648     ATTR2PROP(resolve_rtype);
649     ATTR2PROP(module);
650     ATTR2PROP(period);
651     ATTR2PROP(timeout);
652     ATTR2PROP(disable);
653     ATTR2PROP(filterset);
654   }
655   for(oldconfig = check->children; oldconfig; oldconfig = oldconfig->next)
656     if(!strcmp((char *)oldconfig->name, "config")) break;
657   config = xmlNewNode(NULL, (xmlChar *)"config");
658   if(c) {
659     xmlAttrPtr inherit;
660     if((inherit = xmlHasProp(c, (xmlChar *)"inherit")) != NULL &&
661         inherit->children && inherit->children->content)
662       xmlSetProp(config, (xmlChar *)"inherit", inherit->children->content);
663     for(n = c->children; n; n = n->next) {
664       xmlNsPtr targetns = NULL;
665       xmlChar *v = xmlNodeGetContent(n);
666       if(n->ns) {
667         targetns = xmlSearchNs(parent->doc, xmlDocGetRootElement(parent->doc),
668                                n->ns->prefix);
669         if(!targetns) {
670           targetns = xmlSearchNs(config->doc, config, n->ns->prefix);
671           if(!targetns) targetns = xmlNewNs(config, n->ns->href, n->ns->prefix);
672           noitL(noit_error,"Setting a config value in a new namespace (%p)\n", targetns);
673         }
674         else {
675           noitL(noit_error,"Setting a config value in a namespace (%p)\n", targetns);
676         }
677       }
678       xmlNodePtr co = xmlNewNode(targetns, n->name);
679       if(n->ns && !strcmp((char *)n->name, "value")) {
680         xmlChar *name = xmlGetProp(n, (xmlChar *)"name");
681         if(name) xmlSetProp(co, (xmlChar *)"name", name);
682       }
683       xmlNodeAddContent(co, v);
684       xmlFree(v);
685       xmlAddChild(config, co);
686     }
687   }
688   if(oldconfig) {
689     xmlReplaceNode(oldconfig, config);
690     xmlFreeNode(oldconfig);
691   }
692   else xmlAddChild(check, config);
693   CONF_DIRTY(config);
694 }
695 static xmlNodePtr
696 make_conf_path(char *path) {
697   xmlNodePtr start, tmp;
698   char fullpath[1024], *tok, *brk;
699   if(!path || strlen(path) < 1) return NULL;
700   snprintf(fullpath, sizeof(fullpath), "%s", path+1);
701   fullpath[strlen(fullpath)-1] = '\0';
702   start = noit_conf_get_section(NULL, "/noit/checks");
703   if(!start) return NULL;
704   for (tok = strtok_r(fullpath, "/", &brk);
705        tok;
706        tok = strtok_r(NULL, "/", &brk)) {
707     if(!xmlValidateNameValue((xmlChar *)tok)) return NULL;
708     if(!strcmp(tok, "check")) return NULL;  /* These two paths */
709     if(!strcmp(tok, "config")) return NULL; /* are off limits. */
710     for (tmp = start->children; tmp; tmp = tmp->next) {
711       if(!strcmp((char *)tmp->name, tok)) break;
712     }
713     if(!tmp) {
714       tmp = xmlNewNode(NULL, (xmlChar *)tok);
715       xmlAddChild(start, tmp);
716       CONF_DIRTY(tmp);
717     }
718     start = tmp;
719   }
720   return start;
721 }
722 static int
723 rest_delete_check(noit_http_rest_closure_t *restc,
724                   int npats, char **pats) {
725   noit_http_session_ctx *ctx = restc->http_ctx;
726   xmlXPathObjectPtr pobj = NULL;
727   xmlXPathContextPtr xpath_ctxt = NULL;
728   xmlNodePtr node;
729   uuid_t checkid;
730   noit_check_t *check;
731   const char *error;
732   char xpath[1024], *uuid_conf;
733   int rv, cnt, error_code = 500;
734   noit_boolean exists = noit_false;
735
736   if(npats != 2) goto error;
737
738   if(uuid_parse(pats[1], checkid)) goto error;
739   check = noit_poller_lookup(checkid);
740   if(check)
741     exists = noit_true;
742
743   rv = noit_check_xpath(xpath, sizeof(xpath), pats[0], pats[1]);
744   if(rv == 0) FAIL("uuid not valid");
745   if(rv < 0) FAIL("Tricky McTrickster... No");
746
747   noit_conf_xml_xpath(NULL, &xpath_ctxt);
748   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
749   if(!pobj || pobj->type != XPATH_NODESET ||
750      xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
751     if(exists) { error_code = 403; FAIL("uuid not yours"); }
752     goto not_found;
753   }
754   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
755   if(cnt != 1) FAIL("internal error, |checkid| > 1");
756   node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
757   uuid_conf = (char *)xmlGetProp(node, (xmlChar *)"uuid");
758   if(!uuid_conf || strcasecmp(uuid_conf, pats[1]))
759     FAIL("internal error uuid");
760
761   /* delete this here */
762   if(check) noit_poller_deschedule(check->checkid);
763   CONF_REMOVE(node);
764   xmlUnlinkNode(node);
765   xmlFreeNode(node);
766   noit_conf_mark_changed();
767   if(noit_conf_write_file(NULL) != 0)
768     noitL(noit_error, "local config write failed\n");
769   noit_http_response_ok(ctx, "text/html");
770   noit_http_response_end(ctx);
771   goto cleanup;
772
773  not_found:
774   noit_http_response_not_found(ctx, "text/html");
775   noit_http_response_end(ctx);
776   goto cleanup;
777
778  error:
779   noit_http_response_standard(ctx, error_code, "ERROR", "text/html");
780   noit_http_response_end(ctx);
781   goto cleanup;
782
783  cleanup:
784   if(pobj) xmlXPathFreeObject(pobj);
785   (void)error;
786   return 0;
787 }
788
789 static int
790 rest_set_check(noit_http_rest_closure_t *restc,
791                int npats, char **pats) {
792   noit_http_session_ctx *ctx = restc->http_ctx;
793   xmlXPathObjectPtr pobj = NULL;
794   xmlXPathContextPtr xpath_ctxt = NULL;
795   xmlDocPtr doc = NULL, indoc = NULL;
796   xmlNodePtr node, root, attr, config, parent;
797   uuid_t checkid;
798   noit_check_t *check;
799   char xpath[1024], *uuid_conf;
800   int rv, cnt, error_code = 500, complete = 0, mask = 0;
801   const char *error = "internal error";
802   noit_boolean exists = noit_false;
803
804   if(npats != 2) goto error;
805
806   indoc = rest_get_xml_upload(restc, &mask, &complete);
807   if(!complete) return mask;
808   if(indoc == NULL) FAIL("xml parse error");
809   if(!noit_validate_check_rest_post(indoc, &attr, &config, &error)) goto error;
810
811   if(uuid_parse(pats[1], checkid)) goto error;
812   check = noit_poller_lookup(checkid);
813   if(check)
814     exists = noit_true;
815
816   rv = noit_check_xpath(xpath, sizeof(xpath), pats[0], pats[1]);
817   if(rv == 0) FAIL("uuid not valid");
818   if(rv < 0) FAIL("Tricky McTrickster... No");
819
820   noit_conf_xml_xpath(NULL, &xpath_ctxt);
821   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
822   if(!pobj || pobj->type != XPATH_NODESET ||
823      xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
824     if(exists) { error_code = 403; FAIL("uuid not yours"); }
825     else {
826       char *target = NULL, *name = NULL, *module = NULL;
827       noit_module_t *m = NULL;
828       xmlNodePtr newcheck, a;
829       /* make sure this isn't a dup */
830       for(a = attr->children; a; a = a->next) {
831         if(!strcmp((char *)a->name, "target"))
832           target = (char *)xmlNodeGetContent(a);
833         if(!strcmp((char *)a->name, "name"))
834           name = (char *)xmlNodeGetContent(a);
835         if(!strcmp((char *)a->name, "module"))
836           module = (char *)xmlNodeGetContent(a);
837       }
838       exists = (!target || noit_poller_lookup_by_name(target, name) != NULL);
839       if(module) m = noit_module_lookup(module);
840       if(target) xmlFree(target);
841       if(name) xmlFree(name);
842       if(module) xmlFree(module);
843       if(exists) FAIL("target`name already registered");
844       if(!m) FAIL("module does not exist");
845       /* create a check here */
846       newcheck = xmlNewNode(NULL, (xmlChar *)"check");
847       xmlSetProp(newcheck, (xmlChar *)"uuid", (xmlChar *)pats[1]);
848       parent = make_conf_path(pats[0]);
849       if(!parent) FAIL("invalid path");
850       configure_xml_check(parent, newcheck, attr, config);
851       xmlAddChild(parent, newcheck);
852       CONF_DIRTY(newcheck);
853     }
854   }
855   if(exists) {
856     int module_change;
857     char *target = NULL, *name = NULL, *module = NULL;
858     xmlNodePtr a;
859     noit_check_t *ocheck;
860
861     cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
862     if(cnt != 1) FAIL("internal error, |checkid| > 1");
863     node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
864     uuid_conf = (char *)xmlGetProp(node, (xmlChar *)"uuid");
865     if(!uuid_conf || strcasecmp(uuid_conf, pats[1]))
866       FAIL("internal error uuid");
867
868     /* make sure this isn't a dup */
869     for(a = attr->children; a; a = a->next) {
870       if(!strcmp((char *)a->name, "target"))
871         target = (char *)xmlNodeGetContent(a);
872       if(!strcmp((char *)a->name, "name"))
873         name = (char *)xmlNodeGetContent(a);
874       if(!strcmp((char *)a->name, "module"))
875         module = (char *)xmlNodeGetContent(a);
876     }
877     ocheck = noit_poller_lookup_by_name(target, name);
878     module_change = strcmp(check->module, module);
879     xmlFree(target);
880     xmlFree(name);
881     xmlFree(module);
882     if(ocheck && ocheck != check) FAIL("new target`name would collide");
883     if(module_change) FAIL("cannot change module");
884     parent = make_conf_path(pats[0]);
885     if(!parent) FAIL("invalid path");
886     configure_xml_check(parent, node, attr, config);
887     xmlUnlinkNode(node);
888     xmlAddChild(parent, node);
889     CONF_DIRTY(node);
890   }
891
892   noit_conf_mark_changed();
893   if(noit_conf_write_file(NULL) != 0)
894     noitL(noit_error, "local config write failed\n");
895   noit_poller_reload(xpath);
896   if(restc->call_closure_free) restc->call_closure_free(restc->call_closure);
897   restc->call_closure_free = NULL;
898   restc->call_closure = NULL;
899   if(pobj) xmlXPathFreeObject(pobj);
900   restc->fastpath = rest_show_check;
901   return restc->fastpath(restc, restc->nparams, restc->params);
902
903  error:
904   noit_http_response_standard(ctx, error_code, "ERROR", "text/xml");
905   doc = xmlNewDoc((xmlChar *)"1.0");
906   root = xmlNewDocNode(doc, NULL, (xmlChar *)"error", NULL);
907   xmlDocSetRootElement(doc, root);
908   xmlNodeAddContent(root, (xmlChar *)error);
909   noit_http_response_xml(ctx, doc);
910   noit_http_response_end(ctx);
911   goto cleanup;
912
913  cleanup:
914   if(pobj) xmlXPathFreeObject(pobj);
915   if(doc) xmlFreeDoc(doc);
916   return 0;
917 }
918
919 static int
920 rest_show_config(noit_http_rest_closure_t *restc,
921                  int npats, char **pats) {
922   noit_http_session_ctx *ctx = restc->http_ctx;
923   xmlDocPtr doc = NULL;
924   xmlNodePtr node, root;
925   char xpath[1024];
926
927   snprintf(xpath, sizeof(xpath), "/noit%s", pats ? pats[0] : "");
928   node = noit_conf_get_section(NULL, xpath);
929
930   if(!node) {
931     noit_http_response_not_found(ctx, "text/xml");
932     noit_http_response_end(ctx);
933   }
934   else {
935     doc = xmlNewDoc((xmlChar *)"1.0");
936     root = xmlCopyNode(node, 1);
937     xmlDocSetRootElement(doc, root);
938     noit_http_response_ok(ctx, "text/xml");
939     noit_http_response_xml(ctx, doc);
940     noit_http_response_end(ctx);
941   }
942
943   if(doc) xmlFreeDoc(doc);
944
945   return 0;
946 }
947
948 void
949 noit_check_rest_init() {
950   assert(noit_http_rest_register_auth(
951     "GET", "/", "^config(/.*)?$",
952     rest_show_config, noit_http_rest_client_cert_auth
953   ) == 0);
954   assert(noit_http_rest_register_auth(
955     "GET", "/checks/", "^show(\\.json)?$",
956     rest_show_checks, noit_http_rest_client_cert_auth
957   ) == 0);
958   assert(noit_http_rest_register_auth(
959     "GET", "/checks/", "^show(/.*)(?<=/)(" UUID_REGEX ")(\\.json)?$",
960     rest_show_check, noit_http_rest_client_cert_auth
961   ) == 0);
962   assert(noit_http_rest_register_auth(
963     "PUT", "/checks/", "^set(/.*)(?<=/)(" UUID_REGEX ")$",
964     rest_set_check, noit_http_rest_client_cert_auth
965   ) == 0);
966   assert(noit_http_rest_register_auth(
967     "DELETE", "/checks/", "^delete(/.*)(?<=/)(" UUID_REGEX ")$",
968     rest_delete_check, noit_http_rest_client_cert_auth
969   ) == 0);
970 }
971
Note: See TracBrowser for help on using the browser.