root/src/noit_conf_checks.c

Revision 304ec80b8cf842fc0abe5f9029790908b6455957, 42.3 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 4 days ago)

Convert to libmtev.

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2007, 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 <stdio.h>
37 #include <unistd.h>
38 #include <fcntl.h>
39 #include <errno.h>
40 #include <assert.h>
41 #include <libxml/parser.h>
42 #include <libxml/tree.h>
43 #include <libxml/xpath.h>
44
45 #include <mtev_conf.h>
46 #include <mtev_conf_private.h>
47 #include <mtev_console.h>
48 #include <mtev_hash.h>
49 #include <mtev_log.h>
50
51 #include "noit_filters.h"
52 #include "noit_conf_checks.h"
53 #include "noit_check.h"
54 #include "noit_check_tools.h"
55
56 static void register_console_config_check_commands();
57
58 static struct _valid_attr_t {
59   const char *scope;
60   const char *name;
61   const char *xpath;
62   int checks_fixate;
63 } valid_attrs[] = {
64   { "/checks", "name", "@name", 0 },
65   { "/checks", "target", "@target", 0 },
66   { "/checks", "period", "@period", 0 },
67   { "/checks", "timeout", "@timeout", 0 },
68   { "/checks", "oncheck", "@oncheck", 0 },
69   { "/checks", "disable", "@disable", 0 },
70   { "/checks", "resolve_rtype", "@resolve_rtype", 0 },
71   { "/checks", "filterset", "@filterset", 0 },
72   { "/checks", "module", "@module", 1 },
73   { "/filtersets", "target", "@target", 0 },
74   { "/filtersets", "module", "@module", 0 },
75   { "/filtersets", "name", "@name", 0 },
76   { "/filtersets", "metric", "@metric", 0 },
77 };
78
79 void
80 mtev_console_state_add_check_attrs(mtev_console_state_t *state,
81                                    console_cmd_func_t f,
82                                    const char *scope) {
83   int i;
84   for(i = 0;
85       i < sizeof(valid_attrs)/sizeof(valid_attrs[0]);
86       i++) {
87     if(strcmp(valid_attrs[i].scope, scope)) continue;
88     mtev_console_state_add_cmd(state,
89       NCSCMD(valid_attrs[i].name, f, NULL,
90              NULL, &valid_attrs[i]));
91   }
92 }
93 static mtev_hash_table check_attrs = MTEV_HASH_EMPTY;
94
95 void noit_console_conf_checks_init() {
96   int i;
97   for(i=0;i<sizeof(valid_attrs)/sizeof(*valid_attrs);i++) {
98     mtev_hash_store(&check_attrs,
99                     valid_attrs[i].name, strlen(valid_attrs[i].name),
100                     &valid_attrs[i]);
101   }
102   register_console_config_check_commands();
103 }
104
105 static int
106 noit_console_mkcheck_xpath(char *xpath, int len,
107                            mtev_conf_t_userdata_t *info,
108                            const char *arg) {
109   int rv;
110   rv = noit_check_xpath(xpath, len, "/", arg);
111   if(rv == -1) return -1;
112   if(rv == 0) {
113     char *path = (!info || !strcmp(info->path, "/")) ? "" : info->path;
114     snprintf(xpath, len, "/noit%s%s%s[@uuid]",
115              path, arg ? "/" : "", arg ? arg : "");
116   }
117   return 0;
118 }
119 static void
120 nc_attr_show(mtev_console_closure_t ncct, const char *name, xmlNodePtr cnode,
121              xmlNodePtr anode, const char *value) {
122   char *cpath, *apath;
123   cpath = cnode ? (char *)xmlGetNodePath(cnode) : strdup("");
124   apath = anode ? (char *)xmlGetNodePath(anode) : strdup("");
125   nc_printf(ncct, " %s: %s", name, value ? value : "[undef]");
126   if(value && cpath && apath) {
127     int clen = strlen(cpath);
128     int plen = strlen("/noit/checks/");
129     if(!strncmp(cpath, apath, clen) && apath[clen] == '/') {
130       /* we have a match, which means it isn't inherited */
131     }
132     else {
133       nc_printf(ncct, " [inherited from %s]",
134                 strlen(apath) > plen ? apath + plen : apath);
135     }
136   }
137   nc_write(ncct, "\n", 1);
138   if(cpath) free(cpath);
139   if(apath) free(apath);
140 }
141 static void
142 refresh_subchecks(mtev_console_closure_t ncct,
143                   mtev_conf_t_userdata_t *info) {
144   char *path;
145   char xpath[1024];
146  
147   path = info->path;
148   if(!strcmp(path, "/")) path = "";
149
150   /* The first one is just a process_checks, the second is the reload.
151    * Reload does a lot of work and there is no need to do it twice.
152    */
153   snprintf(xpath, sizeof(xpath), "/noit/%s[@uuid]", path);
154   noit_poller_process_checks(xpath);
155   snprintf(xpath, sizeof(xpath), "/noit/%s//check[@uuid]", path);
156   noit_poller_reload(xpath);
157 }
158 static int
159 noit_config_check_update_attrs(xmlNodePtr node, int argc, char **argv) {
160   int i, error = 0;
161   if(argc % 2) return -1;
162
163   for(i=0; i<argc; i+=2) {
164     void *vattrinfo;
165     struct _valid_attr_t *attrinfo;
166     char *attr = argv[i], *val = NULL;
167     if(!strcasecmp(argv[i], "no")) attr = argv[i+1];
168     else val = argv[i+1];
169     if(!mtev_hash_retrieve(&check_attrs, attr, strlen(attr),
170                            &vattrinfo)) {
171       error = 1;
172       break;
173     }
174     attrinfo = vattrinfo;
175     /* The fixation stuff doesn't matter here, this check is brand-new */
176     xmlUnsetProp(node, (xmlChar *)attrinfo->name);
177     if(val)
178       xmlSetProp(node, (xmlChar *)attrinfo->name, (xmlChar *)val);
179     CONF_DIRTY(node);
180     mtev_conf_mark_changed();
181   }
182   return error;
183 }
184
185 static int
186 noit_conf_mkcheck_under(const char *ppath, int argc, char **argv, uuid_t out) {
187   int rv = -1;
188   const char *path;
189   char xpath[1024];
190   xmlXPathContextPtr xpath_ctxt = NULL;
191   xmlXPathObjectPtr pobj = NULL;
192   xmlNodePtr node = NULL, newnode;
193
194   /* attr val [or] no attr (sets of two) */
195   if(argc % 2) goto out;
196
197   mtev_conf_xml_xpath(NULL, &xpath_ctxt);
198   path = strcmp(ppath, "/") ? ppath : "";
199   snprintf(xpath, sizeof(xpath), "/noit%s", path);
200   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
201   if(!pobj || pobj->type != XPATH_NODESET ||
202      xmlXPathNodeSetGetLength(pobj->nodesetval) != 1) {
203     goto out;
204   }
205   node = (mtev_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
206   if((newnode = xmlNewChild(node, NULL, (xmlChar *)"check", NULL)) != NULL) {
207     char outstr[37];
208     uuid_generate(out);
209     uuid_unparse_lower(out, outstr);
210     xmlSetProp(newnode, (xmlChar *)"uuid", (xmlChar *)outstr);
211     xmlSetProp(newnode, (xmlChar *)"disable", (xmlChar *)"true");
212
213     /* No risk of running off the end (we checked this above) */
214     if(noit_config_check_update_attrs(newnode, argc, argv)) {
215       /* Something went wrong, remove the node */
216       xmlUnlinkNode(newnode);
217     }
218     else {
219       CONF_DIRTY(newnode);
220       mtev_conf_mark_changed();
221       rv = 0;
222     }
223   }
224  out:
225   if(pobj) xmlXPathFreeObject(pobj);
226   return rv;
227 }
228
229 static int
230 noit_console_check(mtev_console_closure_t ncct,
231                    int argc, char **argv,
232                    mtev_console_state_t *state, void *closure) {
233   int cnt;
234   mtev_conf_t_userdata_t *info;
235   char xpath[1024], newuuid_str[37];
236   char *uuid_conf = NULL, *wanted;
237   uuid_t checkid;
238   xmlXPathContextPtr xpath_ctxt = NULL;
239   xmlXPathObjectPtr pobj = NULL;
240   xmlNodePtr node = NULL;
241   mtev_boolean creating_new = mtev_false;
242
243   if(closure) {
244     char *fake_argv[1] = { ".." };
245     mtev_console_state_pop(ncct, 0, argv, NULL, NULL);
246     mtev_console_config_cd(ncct, 1, fake_argv, NULL, NULL);
247   }
248
249   mtev_conf_xml_xpath(NULL, &xpath_ctxt);
250   if(argc < 1) {
251     nc_printf(ncct, "requires at least one argument\n");
252     return -1;
253   }
254   if(argc % 2 == 0) {
255     nc_printf(ncct, "wrong number of arguments\n");
256     return -1;
257   }
258
259   info = mtev_console_userdata_get(ncct, MTEV_CONF_T_USERDATA);
260   wanted = strcmp(argv[0], "new") ? argv[0] : NULL;
261   if(info && !wanted) {
262     /* We are creating a new node */
263     uuid_t out;
264     creating_new = mtev_true;
265     if(strncmp(info->path, "/checks/", strlen("/checks/")) &&
266        strcmp(info->path, "/checks")) {
267       nc_printf(ncct, "New checks must be under /checks/\n");
268       return -1;
269     }
270     if(noit_conf_mkcheck_under(info->path, argc - 1, argv + 1, out)) {
271       nc_printf(ncct, "Error creating new check\n");
272       return -1;
273     }
274     newuuid_str[0] = '\0';
275     uuid_unparse_lower(out, newuuid_str);
276     wanted = newuuid_str;
277   }
278   /* We many not be in conf-t mode -- that's fine */
279   if(noit_console_mkcheck_xpath(xpath, sizeof(xpath), info, wanted)) {
280     nc_printf(ncct, "could not find check '%s'\n", wanted);
281     return -1;
282   }
283
284   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
285   if(!pobj || pobj->type != XPATH_NODESET ||
286      xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
287     nc_printf(ncct, "no checks found for '%s'\n", wanted);
288     goto out;
289   }
290   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
291   if(info && cnt != 1) {
292     nc_printf(ncct, "Ambiguous check specified\n");
293     goto out;
294   }
295   node = (mtev_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
296   uuid_conf = (char *)xmlGetProp(node, (xmlChar *)"uuid");
297   if(!node || !uuid_conf || uuid_parse(uuid_conf, checkid)) {
298     nc_printf(ncct, "%s has invalid or missing UUID!\n",
299               (char *)xmlGetNodePath(node) + strlen("/noit"));
300     goto out;
301   }
302   if(argc > 1 && !creating_new)
303     if(noit_config_check_update_attrs(node, argc - 1, argv + 1))
304       nc_printf(ncct, "Partially successful, error setting some attributes\n");
305
306   if(info) {
307     char *xmlpath;
308     if(info->path) free(info->path);
309     xmlpath = (char *)xmlGetNodePath(node);
310     info->path = strdup(xmlpath + strlen("/noit"));
311     free(xmlpath);
312     uuid_copy(info->current_check, checkid);
313     if(argc > 1) refresh_subchecks(ncct, info);
314     if(state) {
315       mtev_console_state_push_state(ncct, state);
316       mtev_console_state_init(ncct);
317     }
318     goto out;
319   }
320  out:
321   if(uuid_conf) free(uuid_conf);
322   if(pobj) xmlXPathFreeObject(pobj);
323   return 0;
324 }
325 static int
326 noit_console_watch_check(mtev_console_closure_t ncct,
327                          int argc, char **argv,
328                          mtev_console_state_t *state, void *closure) {
329   int i, cnt;
330   int adding = (int)(vpsized_int)closure;
331   int period = 0;
332   char xpath[1024];
333   xmlXPathObjectPtr pobj = NULL;
334   xmlXPathContextPtr xpath_ctxt = NULL;
335
336   mtev_conf_xml_xpath(NULL, &xpath_ctxt);
337   if(argc < 1 || argc > 2) {
338     nc_printf(ncct, "requires one or two arguments\n");
339     return -1;
340   }
341   /* An alternate period */
342   if(argc == 2) period = atoi(argv[1]);
343
344   if(noit_console_mkcheck_xpath(xpath, sizeof(xpath), NULL, argv[0])) {
345     nc_printf(ncct, "ERROR: could not find check '%s'\n", argv[0]);
346     return -1;
347   }
348
349   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
350   if(!pobj || pobj->type != XPATH_NODESET ||
351      xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
352     nc_printf(ncct, "no checks found\n");
353     goto out;
354   }
355   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
356   for(i=0; i<cnt; i++) {
357     uuid_t checkid;
358     noit_check_t *check;
359     xmlNodePtr node;
360     char *uuid_conf;
361
362     node = (mtev_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
363     uuid_conf = (char *)xmlGetProp(node, (xmlChar *)"uuid");
364     if(!uuid_conf || uuid_parse(uuid_conf, checkid)) {
365       nc_printf(ncct, "%s has invalid or missing UUID!\n",
366                 (char *)xmlGetNodePath(node) + strlen("/noit"));
367       continue;
368     }
369     if(period == 0) {
370       check = noit_poller_lookup(checkid);
371       if(!check) continue;
372       if(adding) noit_check_transient_add_feed(check, ncct->feed_path);
373       else noit_check_transient_remove_feed(check, ncct->feed_path);
374     }
375     else {
376       if(adding) {
377         check = noit_check_watch(checkid, period);
378         /* This check must be watched from the console */
379         noit_check_transient_add_feed(check, ncct->feed_path);
380         /* Note the check */
381         noit_check_log_check(check);
382         /* kick it off, if it isn't running already */
383         if(!NOIT_CHECK_LIVE(check)) noit_check_activate(check);
384       }
385       else {
386         check = noit_check_get_watch(checkid, period);
387         if(check) noit_check_transient_remove_feed(check, ncct->feed_path);
388       }
389     }
390   }
391  out:
392   if(pobj) xmlXPathFreeObject(pobj);
393   return 0;
394 }
395 static int
396 _qsort_string_compare(const void *i1, const void *i2) {
397         const char *s1 = ((const char **)i1)[0];
398         const char *s2 = ((const char **)i2)[0];
399         return strcasecmp(s1, s2);
400 }
401 static void
402 nc_print_stat_metrics(mtev_console_closure_t ncct,
403                       noit_check_t *check, stats_t *c) {
404   int mcount=0, cnt=0;
405   const char **sorted_keys;
406   char buff[256];
407   mtev_boolean filtered;
408   mtev_hash_iter iter = MTEV_HASH_ITER_ZERO;
409   const char *k;
410   int klen;
411   void *data;
412
413   memset(&iter, 0, sizeof(iter));
414   while(mtev_hash_next(&c->metrics, &iter, &k, &klen, &data)) cnt++;
415   sorted_keys = malloc(cnt * sizeof(*sorted_keys));
416   memset(&iter, 0, sizeof(iter));
417   while(mtev_hash_next(&c->metrics, &iter, &k, &klen, &data)) {
418     if(sorted_keys && mcount < cnt) sorted_keys[mcount++] = k;
419     else {
420       noit_stats_snprint_metric(buff, sizeof(buff), (metric_t *)data);
421       filtered = !noit_apply_filterset(check->filterset, check, (metric_t *)data);
422       nc_printf(ncct, "  %c%s\n", filtered ? '*' : ' ', buff);
423     }
424   }
425   if(sorted_keys) {
426     int j;
427     qsort(sorted_keys, mcount, sizeof(*sorted_keys),
428           _qsort_string_compare);
429     for(j=0;j<mcount;j++) {
430       if(mtev_hash_retrieve(&c->metrics,
431                             sorted_keys[j], strlen(sorted_keys[j]),
432                             &data)) {
433         noit_stats_snprint_metric(buff, sizeof(buff), (metric_t *)data);
434         filtered = !noit_apply_filterset(check->filterset, check, (metric_t *)data);
435         nc_printf(ncct, "  %c%s\n", filtered ? '*' : ' ', buff);
436       }
437     }
438     free(sorted_keys);
439   }
440 }
441 static int
442 noit_console_show_check(mtev_console_closure_t ncct,
443                         int argc, char **argv,
444                         mtev_console_state_t *state, void *closure) {
445   int i, cnt;
446   mtev_conf_t_userdata_t *info;
447   char xpath[1024];
448   xmlXPathObjectPtr pobj = NULL;
449   xmlXPathContextPtr xpath_ctxt = NULL;
450
451   mtev_conf_xml_xpath(NULL, &xpath_ctxt);
452   if(argc > 1) {
453     nc_printf(ncct, "requires zero or one arguments\n");
454     return -1;
455   }
456
457   info = mtev_console_userdata_get(ncct, MTEV_CONF_T_USERDATA);
458   /* We many not be in conf-t mode -- that's fine */
459   if(noit_console_mkcheck_xpath(xpath, sizeof(xpath), info,
460                                 argc ? argv[0] : NULL)) {
461     nc_printf(ncct, "could not find check '%s'\n", argv[0]);
462     return -1;
463   }
464
465   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
466   if(!pobj || pobj->type != XPATH_NODESET ||
467      xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
468     nc_printf(ncct, "no checks found\n");
469     goto out;
470   }
471   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
472   if(info && cnt != 1) {
473     nc_printf(ncct, "Ambiguous check specified\n");
474     goto out;
475   }
476   for(i=0; i<cnt; i++) {
477     mtev_hash_iter iter = MTEV_HASH_ITER_ZERO;
478     const char *k;
479     int klen;
480     void *data;
481     uuid_t checkid;
482     noit_check_t *check;
483     mtev_hash_table *config;
484     xmlNodePtr node, anode, mnode = NULL;
485     char *uuid_conf;
486     char *module, *value;
487
488     node = (mtev_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
489     uuid_conf = (char *)xmlGetProp(node, (xmlChar *)"uuid");
490     if(!uuid_conf || uuid_parse(uuid_conf, checkid)) {
491       nc_printf(ncct, "%s has invalid or missing UUID!\n",
492                 (char *)xmlGetNodePath(node) + strlen("/noit"));
493       continue;
494     }
495     nc_printf(ncct, "==== %s ====\n", uuid_conf);
496     free(uuid_conf);
497
498 #define MYATTR(a,n,b) _mtev_conf_get_string(node, &(n), "@" #a, &(b))
499 #define INHERIT(a,n,b) \
500   _mtev_conf_get_string(node, &(n), "ancestor-or-self::node()/@" #a, &(b))
501 #define SHOW_ATTR(a) do { \
502   anode = NULL; \
503   value = NULL; \
504   INHERIT(a, anode, value); \
505   nc_attr_show(ncct, #a, node, anode, value); \
506   if(value != NULL) free(value); \
507 } while(0)
508
509     if(!INHERIT(module, mnode, module)) module = NULL;
510     if(MYATTR(name, anode, value)) {
511       nc_printf(ncct, " name: %s\n", value);
512       free(value);
513     }
514     else
515       nc_printf(ncct, " name: %s [from module]\n", module ? module : "[undef]");
516     nc_attr_show(ncct, "module", node, mnode, module);
517     if(module) free(module);
518     SHOW_ATTR(target);
519     SHOW_ATTR(resolve_rtype);
520     SHOW_ATTR(period);
521     SHOW_ATTR(timeout);
522     SHOW_ATTR(oncheck);
523     SHOW_ATTR(filterset);
524     SHOW_ATTR(disable);
525     /* Print out all the config settings */
526     config = mtev_conf_get_hash(node, "config");
527     while(mtev_hash_next(config, &iter, &k, &klen, &data)) {
528       nc_printf(ncct, " config::%s: %s\n", k, (const char *)data);
529     }
530     mtev_hash_destroy(config, free, free);
531     free(config);
532
533     check = noit_poller_lookup(checkid);
534     if(!check) {
535       nc_printf(ncct, " ERROR: not in running system\n");
536     }
537     else {
538       int idx = 0;
539       nc_printf(ncct, " target_ip: %s\n", check->target_ip);
540       nc_printf(ncct, " currently: %08x ", check->flags);
541       if(NOIT_CHECK_RUNNING(check)) { nc_printf(ncct, "running"); idx++; }
542       if(NOIT_CHECK_KILLED(check)) nc_printf(ncct, "%skilled", idx++?",":"");
543       if(!NOIT_CHECK_CONFIGURED(check)) nc_printf(ncct, "%sunconfig", idx++?",":"");
544       if(NOIT_CHECK_DISABLED(check)) nc_printf(ncct, "%sdisabled", idx++?",":"");
545       if(!idx) nc_printf(ncct, "idle");
546       nc_write(ncct, "\n", 1);
547       if (check->fire_event != NULL) {
548         struct timeval now, diff;
549         gettimeofday(&now, NULL);
550         sub_timeval(check->fire_event->whence, now, &diff);
551         nc_printf(ncct, " next run: %0.3f seconds\n",
552                 diff.tv_sec + (diff.tv_usec / 1000000.0));
553       }
554       else {
555         nc_printf(ncct, " next run: unscheduled\n");
556       }
557
558       if(check->stats.current.whence.tv_sec == 0) {
559         nc_printf(ncct, " last run: never\n");
560       }
561       else {
562         stats_t *c = &check->stats.current;
563         struct timeval now, diff;
564         gettimeofday(&now, NULL);
565         sub_timeval(now, c->whence, &diff);
566         nc_printf(ncct, " last run: %0.3f seconds ago\n",
567                   diff.tv_sec + (diff.tv_usec / 1000000.0));
568         nc_printf(ncct, " availability/state: %s/%s\n",
569                   noit_check_available_string(c->available),
570                   noit_check_state_string(c->state));
571         nc_printf(ncct, " status: %s\n", c->status ? c->status : "[[null]]");
572         nc_printf(ncct, " feeds: %d\n", check->feeds ? check->feeds->size : 0);
573       }
574
575       if(mtev_hash_size(&check->stats.inprogress.metrics) > 0) {
576         nc_printf(ncct, " metrics (inprogress):\n");
577         nc_print_stat_metrics(ncct, check, &check->stats.inprogress);
578       }
579       if(mtev_hash_size(&check->stats.current.metrics)) {
580         nc_printf(ncct, " metrics (current):\n");
581         nc_print_stat_metrics(ncct, check, &check->stats.current);
582       } else if(mtev_hash_size(&check->stats.previous.metrics) > 0) {
583         nc_printf(ncct, " metrics (previous):\n");
584         nc_print_stat_metrics(ncct, check, &check->stats.previous);
585       }
586     }
587   }
588  out:
589   if(pobj) xmlXPathFreeObject(pobj);
590   return 0;
591 }
592 static int
593 noit_console_config_nocheck(mtev_console_closure_t ncct,
594                             int argc, char **argv,
595                             mtev_console_state_t *state, void *closure) {
596   int i, cnt;
597   const char *err = "internal error";
598   mtev_conf_t_userdata_t *info;
599   xmlXPathObjectPtr pobj = NULL;
600   xmlXPathContextPtr xpath_ctxt = NULL;
601   char xpath[1024];
602   uuid_t checkid;
603
604   mtev_conf_xml_xpath(NULL, &xpath_ctxt);
605   if(argc < 1) {
606     nc_printf(ncct, "requires one argument\n");
607     return -1;
608   }
609
610   info = mtev_console_userdata_get(ncct, MTEV_CONF_T_USERDATA);
611   if(noit_console_mkcheck_xpath(xpath, sizeof(xpath), info, argv[0])) {
612     nc_printf(ncct, "could not find check '%s'\n", argv[0]);
613     return -1;
614   }
615   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
616   if(!pobj || pobj->type != XPATH_NODESET ||
617      xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
618     err = "no checks found";
619     goto bad;
620   }
621   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
622   for(i=0; i<cnt; i++) {
623     xmlNodePtr node;
624     char *uuid_conf;
625     node = (mtev_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
626     uuid_conf = (char *)xmlGetProp(node, (xmlChar *)"uuid");
627     if(!uuid_conf || uuid_parse(uuid_conf, checkid)) {
628       nc_printf(ncct, "%s has invalid or missing UUID!\n",
629                 (char *)xmlGetNodePath(node) + strlen("/noit"));
630     }
631     else {
632       if(argc > 1) {
633         int j;
634         for(j=1;j<argc;j++)
635           xmlUnsetProp(node, (xmlChar *)argv[j]);
636         CONF_DIRTY(node);
637       } else {
638         nc_printf(ncct, "descheduling %s\n", uuid_conf);
639         noit_poller_deschedule(checkid);
640         CONF_REMOVE(node);
641         xmlUnlinkNode(node);
642       }
643       mtev_conf_mark_changed();
644     }
645   }
646   if(argc > 1) {
647     noit_poller_process_checks(xpath);
648     noit_poller_reload(xpath);
649   }
650   nc_printf(ncct, "rebuilding causal map...\n");
651   noit_poller_make_causal_map();
652   if(pobj) xmlXPathFreeObject(pobj);
653   return 0;
654  bad:
655   if(pobj) xmlXPathFreeObject(pobj);
656   nc_printf(ncct, "%s\n", err);
657   return -1;
658 }
659 static int
660 noit_console_config_show(mtev_console_closure_t ncct,
661                          int argc, char **argv,
662                          mtev_console_state_t *state, void *closure) {
663   mtev_hash_iter iter = MTEV_HASH_ITER_ZERO;
664   const char *k;
665   int klen;
666   void *data;
667   int i, cnt, titled = 0, cliplen = 0;
668   const char *path = "", *basepath = NULL;
669   char xpath[1024];
670   mtev_conf_t_userdata_t *info = NULL;
671   mtev_hash_table *config;
672   xmlXPathObjectPtr pobj = NULL;
673   xmlXPathContextPtr xpath_ctxt = NULL, current_ctxt;
674   xmlDocPtr master_config = NULL;
675   xmlNodePtr node = NULL;
676
677   mtev_conf_xml_xpath(&master_config, &xpath_ctxt);
678   if(argc > 1) {
679     nc_printf(ncct, "too many arguments\n");
680     return -1;
681   }
682
683   info = mtev_console_userdata_get(ncct, MTEV_CONF_T_USERDATA);
684   if(info && info->path) path = basepath = info->path;
685   if(!info && argc == 0) {
686     nc_printf(ncct, "argument required when not in configuration mode\n");
687     return -1;
688   }
689
690   if(argc == 1) path = argv[0];
691   if(!basepath) basepath = path;
692
693   /* { / } is a special case */
694   if(!strcmp(basepath, "/")) basepath = "";
695   if(!strcmp(path, "/")) path = "";
696
697   if(!master_config) {
698     nc_printf(ncct, "no config\n");
699     return -1;
700   }
701
702   /* { / } is the only path that will end with a /
703    * in XPath { / / * } means something _entirely different than { / * }
704    * Ever notice how it is hard to describe xpath in C comments?
705    */
706   /* We don't want to show the root node */
707   cliplen = strlen("/noit/");
708
709   /* If we are in configuration mode
710    * and we are without an argument or the argument is absolute,
711    * clip the current path off */
712   if(info && (argc == 0 || path[0] != '/')) cliplen += strlen(basepath);
713   if(!path[0] || path[0] == '/') /* base only, or absolute path requested */
714     snprintf(xpath, sizeof(xpath), "/noit%s/@*", path);
715   else
716     snprintf(xpath, sizeof(xpath), "/noit%s/%s/@*", basepath, path);
717
718   current_ctxt = xpath_ctxt;
719   pobj = xmlXPathEval((xmlChar *)xpath, current_ctxt);
720   if(!pobj || pobj->type != XPATH_NODESET) {
721     nc_printf(ncct, "no such object\n");
722     goto bad;
723   }
724   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
725   titled = 0;
726   for(i=0; i<cnt; i++) {
727     node = (mtev_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
728     if(!strcmp((char *)node->name, "check")) continue;
729     if(node->children && node->children == xmlGetLastChild(node) &&
730       xmlNodeIsText(node->children)) {
731       char *node_str, *xmlpath;
732       node_str = (char *)xmlXPathCastNodeToString(node->children);
733       xmlpath = (char *)xmlGetNodePath(node);
734       if(!titled++) nc_printf(ncct, "== Section Settings ==\n");
735       nc_printf(ncct, "%s: %s\n", xmlpath + cliplen, node_str);
736       free(xmlpath);
737       free(node_str);
738     }
739   }
740   xmlXPathFreeObject(pobj);
741
742   /* Print out all the config settings */
743   if(!path[0] || path[0] == '/') /* base only, or absolute path requested */
744     snprintf(xpath, sizeof(xpath), "/noit%s", path);
745   else
746     snprintf(xpath, sizeof(xpath), "/noit%s/%s", basepath, path);
747   pobj = xmlXPathEval((xmlChar *)xpath, current_ctxt);
748   if(!pobj || pobj->type != XPATH_NODESET) {
749     nc_printf(ncct, "no such object\n");
750     goto bad;
751   }
752   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
753   if(cnt > 0) {
754     node = (mtev_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
755     titled = 0;
756     config = mtev_conf_get_hash(node, "config");
757     while(mtev_hash_next(config, &iter, &k, &klen, &data)) {
758       if(!titled++) nc_printf(ncct, "== Section [Aggregated] Config ==\n");
759       nc_printf(ncct, "config::%s: %s\n", k, (const char *)data);
760     }
761     mtev_hash_destroy(config, free, free);
762     free(config);
763   }
764   xmlXPathFreeObject(pobj);
765
766   /* _shorten string_ turning last { / @ * } to { / * } */
767   if(!path[0] || path[0] == '/') /* base only, or absolute path requested */
768     snprintf(xpath, sizeof(xpath), "/noit%s/*", path);
769   else
770     snprintf(xpath, sizeof(xpath), "/noit%s/%s/*", basepath, path);
771   pobj = xmlXPathEval((xmlChar *)xpath, current_ctxt);
772   if(!pobj || pobj->type != XPATH_NODESET) {
773     nc_printf(ncct, "no such object\n");
774     goto bad;
775   }
776   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
777   titled = 0;
778   for(i=0; i<cnt; i++) {
779     char *xmlpath;
780     node = (mtev_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
781     if(!strcmp((char *)node->name, "check")) continue;
782     if(!strcmp((char *)node->name, "filterset")) continue;
783     xmlpath = (char *)xmlGetNodePath(node);
784     if(strcmp(xmlpath + cliplen, "config")) {
785       if(!(node->children && node->children == xmlGetLastChild(node) &&
786            xmlNodeIsText(node->children))) {
787         if(!titled++) nc_printf(ncct, "== Subsections ==\n");
788         nc_printf(ncct, "%s\n", xmlpath + cliplen);
789       }
790     }
791     free(xmlpath);
792   }
793
794   titled = 0;
795   for(i=0; i<cnt; i++) {
796     node = (mtev_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
797     if(!strcmp((char *)node->name, "filterset")) {
798       xmlAttr *attr;
799       char *filter_name = NULL;
800       for(attr=node->properties; attr; attr = attr->next) {
801         if(!strcmp((char *)attr->name, "name"))
802           filter_name = (char *)xmlXPathCastNodeToString(attr->children);
803       }
804       if(filter_name) {
805         nc_printf(ncct, "filterset[@name=\"%s\"]\n", filter_name);
806         xmlFree(filter_name);
807       }
808       else nc_printf(ncct, "fitlerset\n");
809     }
810     else if(!strcmp((char *)node->name, "check")) {
811       int busted = 1;
812       xmlAttr *attr;
813       char *uuid_str = NULL;
814       uuid_t checkid;
815
816       if(!titled++) nc_printf(ncct, "== Checks ==\n");
817
818       for(attr=node->properties; attr; attr = attr->next) {
819         if(!strcmp((char *)attr->name, "uuid")) {
820           uuid_str = (char *)xmlXPathCastNodeToString(attr->children);
821           break;
822         }
823       }
824       nc_printf(ncct, "check[@uuid=\"%s\"] ", uuid_str ? uuid_str : "undefined");
825       if(uuid_str && uuid_parse(uuid_str, checkid) == 0) {
826         noit_check_t *check;
827         check = noit_poller_lookup(checkid);
828         if(check) {
829           busted = 0;
830           nc_printf(ncct, "%s`%s`%s", check->target, check->module, check->name);
831         }
832       }
833       if(uuid_str) free(uuid_str);
834       if(busted) nc_printf(ncct, "[check not in running system]");
835       nc_write(ncct, "\n", 1);
836     }
837   }
838   xmlXPathFreeObject(pobj);
839   return 0;
840  bad:
841   if(pobj) xmlXPathFreeObject(pobj);
842   return -1;
843 }
844
845 static char *
846 conf_t_check_prompt(EditLine *el) {
847   mtev_console_closure_t ncct;
848   mtev_conf_t_userdata_t *info;
849   noit_check_t *check;
850   static char *tl = "noit(conf)# ";
851   static char *pfmt = "noit(conf:%s%s%s)# ";
852
853   el_get(el, EL_USERDATA, (void *)&ncct);
854   if(!ncct) return tl;
855   info = mtev_console_userdata_get(ncct, MTEV_CONF_T_USERDATA);
856   if(!info) return tl;
857
858   check = noit_poller_lookup(info->current_check);
859   if(check &&
860      check->target && check->target[0] &&
861      check->name && check->name[0])
862     snprintf(info->prompt, sizeof(info->prompt),
863              pfmt, check->target, "`", check->name);
864   else {
865     char uuid_str[37];
866     uuid_unparse_lower(info->current_check, uuid_str);
867     snprintf(info->prompt, sizeof(info->prompt), pfmt, "[", uuid_str, "]");
868   }
869   return info->prompt;
870 }
871 static int
872 noit_conf_checks_reload(mtev_console_closure_t ncct,
873                         int argc, char **argv,
874                         mtev_console_state_t *state, void *closure) {
875   if(mtev_conf_reload(ncct, argc, argv, state, closure)) return -1;
876   noit_poller_reload(NULL);
877   return 0;
878 }
879
880 static int
881 validate_attr_set_scope(mtev_conf_t_userdata_t *info,
882                         struct _valid_attr_t *attrinfo) {
883   int len;
884   len = strlen(attrinfo->scope);
885   if(strncmp(info->path, attrinfo->scope, len) ||
886      (info->path[len] != '\0' && info->path[len] != '/')) {
887     return -1;
888   }
889   return 0;
890 }
891 static int
892 replace_config(mtev_console_closure_t ncct,
893                mtev_conf_t_userdata_t *info, const char *name,
894                const char *value) {
895   int i, cnt, rv = -1, active = 0;
896   xmlXPathObjectPtr pobj = NULL;
897   xmlXPathContextPtr xpath_ctxt = NULL;
898   xmlNodePtr node, confignode;
899   char xpath[1024], *path;
900
901   path = info->path;
902   if(!strcmp(path, "/")) path = "";
903
904   mtev_conf_xml_xpath(NULL, &xpath_ctxt);
905
906   /* Only if checks will fixate this attribute shall we check for
907    * child <check> nodes.
908    * NOTE: this return nothing and "seems" okay if we are _in_
909    *       a <check> node.  That case is handled below.
910    */
911   snprintf(xpath, sizeof(xpath), "/noit/%s//check[@uuid]", path);
912   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
913   if(!pobj || pobj->type != XPATH_NODESET) goto out;
914   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
915   for(i=0; i<cnt; i++) {
916     uuid_t checkid;
917     node = (mtev_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
918     if(mtev_conf_get_uuid(node, "@uuid", checkid)) {
919       noit_check_t *check;
920       check = noit_poller_lookup(checkid);
921       if(check && NOIT_CHECK_LIVE(check)) active++;
922     }
923   }
924   if(pobj) xmlXPathFreeObject(pobj);
925
926 #ifdef UNSAFE_RECONFIG
927   snprintf(xpath, sizeof(xpath), "/noit/%s", path);
928   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
929   if(!pobj || pobj->type != XPATH_NODESET) goto out;
930   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
931   if(cnt != 1) {
932     nc_printf(ncct, "Internal error: context node disappeared\n");
933     goto out;
934   }
935   node = (mtev_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
936   if(strcmp((const char *)node->name, "check")) {
937     uuid_t checkid;
938     /* Detect if  we are actually a <check> node and attempting to
939      * change something we shouldn't.
940      * This is the counterpart noted above.
941      */
942     if(mtev_conf_get_uuid(node, "@uuid", checkid)) {
943       noit_check_t *check;
944       check = noit_poller_lookup(checkid);
945       if(NOIT_CHECK_LIVE(check)) active++;
946     }
947   }
948   if(active) {
949     nc_printf(ncct, "Cannot set '%s', it would effect %d live check(s)\n",
950               name, active);
951     goto out;
952   }
953   if(pobj) xmlXPathFreeObject(pobj);
954 #endif
955
956   /* Here we want to remove /noit/path/config/name */
957   snprintf(xpath, sizeof(xpath), "/noit/%s/config/%s", path, name);
958   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
959   if(!pobj || pobj->type != XPATH_NODESET) goto out;
960   if(xmlXPathNodeSetGetLength(pobj->nodesetval) > 0) {
961     xmlNodePtr toremove;
962     toremove = xmlXPathNodeSetItem(pobj->nodesetval, 0);
963     CONF_REMOVE(toremove);
964     xmlUnlinkNode(toremove);
965   }
966   /* TODO: if there are no more children of config, remove config? */
967   if(value) {
968     if(pobj) xmlXPathFreeObject(pobj);
969     /* He we create config if needed and place a child node under it */
970     snprintf(xpath, sizeof(xpath), "/noit/%s/config", path);
971     pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
972     if(!pobj || pobj->type != XPATH_NODESET) goto out;
973     if(xmlXPathNodeSetGetLength(pobj->nodesetval) == 0) {
974       if(pobj) xmlXPathFreeObject(pobj);
975       snprintf(xpath, sizeof(xpath), "/noit/%s", path);
976       pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
977       if(!pobj || pobj->type != XPATH_NODESET) goto out;
978       if(xmlXPathNodeSetGetLength(pobj->nodesetval) != 1) {
979         nc_printf(ncct, "Node disappeared from under you!\n");
980         goto out;
981       }
982       confignode = xmlNewChild(xmlXPathNodeSetItem(pobj->nodesetval, 0),
983                                NULL, (xmlChar *)"config", NULL);
984       if(confignode == NULL) {
985         nc_printf(ncct, "Error creating config child node.\n");
986         goto out;
987       }
988     }
989     else confignode = xmlXPathNodeSetItem(pobj->nodesetval, 0);
990
991     assert(confignode);
992     /* Now we create a child */
993     xmlNewChild(confignode, NULL, (xmlChar *)name, (xmlChar *)value);
994     CONF_DIRTY(confignode);
995   }
996   mtev_conf_mark_changed();
997   rv = 0;
998  out:
999   if(pobj) xmlXPathFreeObject(pobj);
1000   return rv;
1001 }
1002 static int
1003 replace_attr(mtev_console_closure_t ncct,
1004              mtev_conf_t_userdata_t *info, struct _valid_attr_t *attrinfo,
1005              const char *value) {
1006   int i, cnt, rv = -1, active = 0;
1007   xmlXPathObjectPtr pobj = NULL;
1008   xmlXPathContextPtr xpath_ctxt = NULL;
1009   xmlNodePtr node;
1010   char xpath[1024], *path;
1011
1012   path = info->path;
1013   if(!strcmp(path, "/")) path = "";
1014
1015   mtev_conf_xml_xpath(NULL, &xpath_ctxt);
1016   if(attrinfo->checks_fixate) {
1017     /* Only if checks will fixate this attribute shall we check for
1018      * child <check> nodes.
1019      * NOTE: this return nothing and "seems" okay if we are _in_
1020      *       a <check> node.  That case is handled below.
1021      */
1022     snprintf(xpath, sizeof(xpath), "/noit/%s//check[@uuid]", path);
1023     pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
1024     if(!pobj || pobj->type != XPATH_NODESET) goto out;
1025     cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
1026     for(i=0; i<cnt; i++) {
1027       uuid_t checkid;
1028       node = (mtev_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
1029       if(mtev_conf_get_uuid(node, "@uuid", checkid)) {
1030         noit_check_t *check;
1031         check = noit_poller_lookup(checkid);
1032         if(check && NOIT_CHECK_LIVE(check)) active++;
1033       }
1034     }
1035     if(pobj) xmlXPathFreeObject(pobj);
1036   }
1037   snprintf(xpath, sizeof(xpath), "/noit/%s", path);
1038   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
1039   if(!pobj || pobj->type != XPATH_NODESET) goto out;
1040   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
1041   if(cnt != 1) {
1042     nc_printf(ncct, "Internal error: context node disappeared\n");
1043     goto out;
1044   }
1045   node = (mtev_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
1046   if(attrinfo->checks_fixate &&
1047      !strcmp((const char *)node->name, "check")) {
1048     uuid_t checkid;
1049     /* Detect if  we are actually a <check> node and attempting to
1050      * change something we shouldn't.
1051      * This is the counterpart noted above.
1052      */
1053     if(mtev_conf_get_uuid(node, "@uuid", checkid)) {
1054       noit_check_t *check;
1055       check = noit_poller_lookup(checkid);
1056       if(check && NOIT_CHECK_LIVE(check)) active++;
1057     }
1058   }
1059   if(active) {
1060     nc_printf(ncct, "Cannot set '%s', it would effect %d live check(s)\n",
1061               attrinfo->name, active);
1062     goto out;
1063   }
1064   xmlUnsetProp(node, (xmlChar *)attrinfo->name);
1065   if(value)
1066     xmlSetProp(node, (xmlChar *)attrinfo->name, (xmlChar *)value);
1067   CONF_DIRTY(node);
1068   mtev_conf_mark_changed();
1069   rv = 0;
1070  out:
1071   if(pobj) xmlXPathFreeObject(pobj);
1072   return rv;
1073 }
1074 int
1075 noit_conf_check_set_attr(mtev_console_closure_t ncct,
1076                          int argc, char **argv,
1077                          mtev_console_state_t *state, void *closure) {
1078   struct _valid_attr_t *attrinfo = closure;
1079   mtev_conf_t_userdata_t *info;
1080
1081   info = mtev_console_userdata_get(ncct, MTEV_CONF_T_USERDATA);
1082   if(!info || validate_attr_set_scope(info, attrinfo)) {
1083     nc_printf(ncct, "'%s' attribute only valid in %s scope\n",
1084               attrinfo->name, attrinfo->scope);
1085     return -1;
1086   }
1087
1088   if(argc != 1) {
1089     nc_printf(ncct, "set requires exactly one value\n");
1090     return -1;
1091   }
1092   /* Okay, we have an attribute and it should be set/replaced on the
1093    * current path.
1094    */
1095   if(replace_attr(ncct, info, attrinfo, argv[0])) {
1096     return -1;
1097   }
1098
1099   /* So, we updated an attribute, so we need to reload all checks
1100    * that are descendent-or-self of this node.
1101    */
1102   if(!strncmp(info->path, "/checks", strlen("/checks")))
1103     refresh_subchecks(ncct, info);
1104   if(!strncmp(info->path, "/filtersets", strlen("/filtersets")))
1105     noit_refresh_filtersets(ncct, info);
1106   return 0;
1107 }
1108
1109 int
1110 noit_conf_check_unset_attr(mtev_console_closure_t ncct,
1111                            int argc, char **argv,
1112                            mtev_console_state_t *state, void *closure) {
1113   struct _valid_attr_t *attrinfo = closure;
1114   mtev_conf_t_userdata_t *info;
1115
1116   info = mtev_console_userdata_get(ncct, MTEV_CONF_T_USERDATA);
1117   if(!info || validate_attr_set_scope(info, attrinfo)) {
1118     nc_printf(ncct, "'%s' attribute only valid in %s scope\n",
1119               attrinfo->name, attrinfo->scope);
1120     return -1;
1121   }
1122
1123   if(argc != 0) {
1124     nc_printf(ncct, "no arguments allowed to this command.\n");
1125     return -1;
1126   }
1127   /* Okay, we have an attribute and it should be set/replaced on the
1128    * current path.
1129    */
1130   if(replace_attr(ncct, info, attrinfo, NULL)) {
1131     return -1;
1132   }
1133
1134   /* So, we updated an attribute, so we need to reload all checks
1135    * that are descendent-or-self of this node.
1136    */
1137   if(!strncmp(info->path, "/checks", strlen("/checks")))
1138     refresh_subchecks(ncct, info);
1139   if(!strncmp(info->path, "/filterset", strlen("/filterest")))
1140     noit_refresh_filtersets(ncct, info);
1141   return 0;
1142 }
1143
1144 int
1145 noit_console_config_setconfig(mtev_console_closure_t ncct,
1146                                 int argc, char **argv,
1147                                 mtev_console_state_t *state, void *closure) {
1148   mtev_conf_t_userdata_t *info;
1149
1150   info = mtev_console_userdata_get(ncct, MTEV_CONF_T_USERDATA);
1151
1152   if(argc != 2) {
1153     nc_printf(ncct, "two arguments required.\n");
1154     return -1;
1155   }
1156   /* Okay, we have an child name and it should be culled from
1157    * current path/config.
1158    */
1159   if(replace_config(ncct, info, argv[0], argv[1])) {
1160     return -1;
1161   }
1162
1163   /* So, we updated an attribute, so we need to reload all checks
1164    * that are descendent-or-self of this node.
1165    */
1166   refresh_subchecks(ncct, info);
1167   return 0;
1168 }
1169
1170 int
1171 noit_console_config_unsetconfig(mtev_console_closure_t ncct,
1172                                 int argc, char **argv,
1173                                 mtev_console_state_t *state, void *closure) {
1174   mtev_conf_t_userdata_t *info;
1175
1176   info = mtev_console_userdata_get(ncct, MTEV_CONF_T_USERDATA);
1177
1178   if(argc != 1) {
1179     nc_printf(ncct, "one argument required.\n");
1180     return -1;
1181   }
1182   /* Okay, we have an child name and it should be culled from
1183    * current path/config.
1184    */
1185   if(replace_config(ncct, info, argv[0], NULL)) {
1186     return -1;
1187   }
1188
1189   /* So, we updated an attribute, so we need to reload all checks
1190    * that are descendent-or-self of this node.
1191    */
1192   refresh_subchecks(ncct, info);
1193   return 0;
1194 }
1195
1196
1197 #define NEW_STATE(a) (a) = mtev_console_state_alloc()
1198 #define ADD_CMD(a,cmd,func,ac,ss,c) \
1199   mtev_console_state_add_cmd((a), \
1200     NCSCMD(cmd, func, ac, ss, c))
1201 #define DELEGATE_CMD(a,cmd,ac,ss) \
1202   mtev_console_state_add_cmd((a), \
1203     NCSCMD(cmd, mtev_console_state_delegate, ac, ss, NULL))
1204
1205 static
1206 void register_console_config_check_commands() {
1207   cmd_info_t *showcmd, *nocmd, *confcmd, *conftcmd, *conftnocmd, *lscmd;
1208   mtev_console_state_t *tl, *_conf_t_check_state, *_unset_state,
1209                        *_attr_state, *_uattr_state;
1210
1211   tl = mtev_console_state_initial();
1212   showcmd = mtev_console_state_get_cmd(tl, "show");
1213   nocmd = mtev_console_state_get_cmd(tl, "no");
1214   confcmd = mtev_console_state_get_cmd(tl, "configure");
1215   conftcmd = mtev_console_state_get_cmd(confcmd->dstate, "terminal");
1216   conftnocmd = mtev_console_state_get_cmd(conftcmd->dstate, "no");
1217   lscmd = mtev_console_state_get_cmd(conftcmd->dstate, "ls");
1218   lscmd->func = noit_console_config_show;
1219   /* attribute <attrname> <value> */
1220   NEW_STATE(_attr_state);
1221   mtev_console_state_add_check_attrs(_attr_state, noit_conf_check_set_attr,
1222                                      "/checks");
1223  
1224   /* no attribute <attrname> <value> */
1225   NEW_STATE(_uattr_state);
1226   mtev_console_state_add_check_attrs(_uattr_state, noit_conf_check_unset_attr,
1227                                      "/checks");
1228   NEW_STATE(_unset_state);
1229   DELEGATE_CMD(_unset_state, "attribute",
1230                mtev_console_opt_delegate, _uattr_state);
1231   ADD_CMD(_unset_state, "config",
1232           noit_console_config_unsetconfig, NULL, NULL, NULL);
1233
1234   DELEGATE_CMD(conftnocmd->dstate, "attribute",
1235                mtev_console_opt_delegate, _uattr_state);
1236   ADD_CMD(conftnocmd->dstate, "config",
1237           noit_console_config_unsetconfig, NULL, NULL, NULL);
1238   ADD_CMD(conftnocmd->dstate, "check",
1239           noit_console_config_nocheck, NULL, NULL, NULL);
1240  
1241   NEW_STATE(_conf_t_check_state);
1242   _conf_t_check_state->console_prompt_function = conf_t_check_prompt;
1243   DELEGATE_CMD(_conf_t_check_state, "attribute",
1244                mtev_console_opt_delegate, _attr_state);
1245   DELEGATE_CMD(_conf_t_check_state, "no",
1246                mtev_console_opt_delegate, _unset_state);
1247   ADD_CMD(_conf_t_check_state, "config",
1248           noit_console_config_setconfig, NULL, NULL, NULL);
1249   ADD_CMD(_conf_t_check_state, "status",
1250           noit_console_show_check, NULL, NULL, NULL);
1251   ADD_CMD(_conf_t_check_state, "exit",
1252           mtev_console_config_cd, NULL, NULL, "..");
1253   ADD_CMD(_conf_t_check_state, "check",
1254           noit_console_check, noit_console_conf_check_opts,
1255           _conf_t_check_state, "..");
1256
1257   ADD_CMD(conftcmd->dstate, "config",
1258           noit_console_config_setconfig, NULL, NULL, NULL);
1259   ADD_CMD(conftcmd->dstate, "check",
1260           noit_console_check, noit_console_conf_check_opts,
1261           _conf_t_check_state, NULL);
1262
1263   ADD_CMD(showcmd->dstate, "check",
1264           noit_console_show_check, noit_console_check_opts, NULL, NULL);
1265
1266   ADD_CMD(tl, "watch",
1267           noit_console_watch_check, noit_console_check_opts, NULL, (void *)1);
1268
1269   ADD_CMD(nocmd->dstate, "watch",
1270           noit_console_watch_check, noit_console_check_opts, NULL, (void *)0);
1271
1272   DELEGATE_CMD(conftcmd->dstate, "attribute",
1273                mtev_console_opt_delegate, _attr_state);
1274
1275   ADD_CMD(tl, "reload", noit_conf_checks_reload, NULL, NULL, NULL);
1276 }
1277
Note: See TracBrowser for help on using the browser.