root/src/noit_conf_checks.c

Revision 94eee0ed35ac7112cd855488e6a2240f9169d335, 42.2 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 1 year ago)

use concurrency kit for hash tables

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