root/src/noit_conf_checks.c

Revision 37daa37bbc546da8db4d76945b9c4b523e02d77b, 46.7 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 9 years ago)

online configuration filters, refs #182

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