root/src/noit_check_rest.c

Revision 0d0550908faa7fedd44adc8e65b578299ce5bf95, 34.2 kB (checked in by Aidan Cully <aidan.cully@circonus.com>, 1 month ago)

fix make_conf_path's null-termination of local fullpath variable.

was using strlen(fullpath) where sizeof(fullpath) was intended.
fixed.

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