root/src/noit_conf_checks.c

Revision 02569455fdf31dd93db18514b0b4cda5d5181ff7, 45.9 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 5 years ago)

work on #171, getting check info works

  • Property mode set to 100644
Line 
1
2 /*
3  * Copyright (c) 2007, OmniTI Computer Consulting, Inc.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are
8  * met:
9  *
10  *     * Redistributions of source code must retain the above copyright
11  *       notice, this list of conditions and the following disclaimer.
12  *     * Redistributions in binary form must reproduce the above
13  *       copyright notice, this list of conditions and the following
14  *       disclaimer in the documentation and/or other materials provided
15  *       with the distribution.
16  *     * Neither the name OmniTI Computer Consulting, Inc. nor the names
17  *       of its contributors may be used to endorse or promote products
18  *       derived from this software without specific prior written
19  *       permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32  */
33
34 #include "noit_defines.h"
35
36 #include <stdio.h>
37 #include <unistd.h>
38 #include <fcntl.h>
39 #include <errno.h>
40 #include <assert.h>
41 #include <libxml/parser.h>
42 #include <libxml/tree.h>
43 #include <libxml/xpath.h>
44
45 #include "noit_conf.h"
46 #include "noit_conf_private.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 static int noit_console_config_cd(noit_console_closure_t ncct,
56                                   int argc, char **argv,
57                                   noit_console_state_t *state, void *closure);
58
59 static struct _valid_attr_t {
60   const char *scope;
61   const char *name;
62   const char *xpath;
63   int checks_fixate;
64 } valid_attrs[] = {
65   { "/checks", "name", "@name", 0 },
66   { "/checks", "target", "@target", 0 },
67   { "/checks", "period", "@period", 0 },
68   { "/checks", "timeout", "@timeout", 0 },
69   { "/checks", "oncheck", "@oncheck", 0 },
70   { "/checks", "disable", "@disable", 0 },
71   { "/checks", "filterset", "@filterset", 0 },
72   { "/checks", "module", "@module", 1 },
73   { "/filtersets/filterset", "target", "@target", 1 },
74   { "/filtersets/filterset", "module", "@module", 1 },
75   { "/filtersets/filterset", "name", "@name", 1 },
76   { "/filtersets/filterset", "metric", "@metric", 1 },
77 };
78
79 void
80 noit_console_state_add_check_attrs(noit_console_state_t *state,
81                                    console_cmd_func_t f) {
82   int i;
83   for(i = 0;
84       i < sizeof(valid_attrs)/sizeof(valid_attrs[0]);
85       i++) {
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 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(noit_conf_mkcheck_under(info->path, argc - 1, argv + 1, out)) {
268       nc_printf(ncct, "Error creating new check\n");
269       return -1;
270     }
271     newuuid_str[0] = '\0';
272     uuid_unparse_lower(out, newuuid_str);
273     wanted = newuuid_str;
274   }
275   /* We many not be in conf-t mode -- that's fine */
276   if(noit_console_mkcheck_xpath(xpath, sizeof(xpath), info, wanted)) {
277     nc_printf(ncct, "could not find check '%s'\n", wanted);
278     return -1;
279   }
280
281   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
282   if(!pobj || pobj->type != XPATH_NODESET ||
283      xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
284     nc_printf(ncct, "no checks found for '%s'\n", wanted);
285     goto out;
286   }
287   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
288   if(info && cnt != 1) {
289     nc_printf(ncct, "Ambiguous check specified\n");
290     goto out;
291   }
292   node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
293   uuid_conf = (char *)xmlGetProp(node, (xmlChar *)"uuid");
294   if(!uuid_conf || uuid_parse(uuid_conf, checkid)) {
295     nc_printf(ncct, "%s has invalid or missing UUID!\n",
296               (char *)xmlGetNodePath(node) + strlen("/noit"));
297     goto out;
298   }
299   if(argc > 1 && !creating_new)
300     if(noit_config_check_update_attrs(node, argc - 1, argv + 1))
301       nc_printf(ncct, "Partially successful, error setting some attributes\n");
302
303   if(info) {
304     if(info->path) free(info->path);
305     info->path = strdup((char *)xmlGetNodePath(node) + strlen("/noit"));
306     uuid_copy(info->current_check, checkid);
307     if(argc > 1) refresh_subchecks(ncct, info);
308     if(state) {
309       noit_console_state_push_state(ncct, state);
310       noit_console_state_init(ncct);
311     }
312     goto out;
313   }
314  out:
315   if(pobj) xmlXPathFreeObject(pobj);
316   return 0;
317 }
318 static int
319 noit_console_watch_check(noit_console_closure_t ncct,
320                          int argc, char **argv,
321                          noit_console_state_t *state, void *closure) {
322   int i, cnt;
323   int adding = (int)closure;
324   int period = 0;
325   char xpath[1024];
326   xmlXPathObjectPtr pobj = NULL;
327   xmlXPathContextPtr xpath_ctxt = NULL;
328
329   noit_conf_xml_xpath(NULL, &xpath_ctxt);
330   if(argc < 1 || argc > 2) {
331     nc_printf(ncct, "requires one or two arguments\n");
332     return -1;
333   }
334   /* An alternate period */
335   if(argc == 2) period = atoi(argv[1]);
336
337   if(noit_console_mkcheck_xpath(xpath, sizeof(xpath), NULL,
338                                 argc ? argv[0] : NULL)) {
339     nc_printf(ncct, "ERROR: could not find check '%s'\n", argv[0]);
340     return -1;
341   }
342
343   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
344   if(!pobj || pobj->type != XPATH_NODESET ||
345      xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
346     nc_printf(ncct, "no checks found\n");
347     goto out;
348   }
349   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
350   for(i=0; i<cnt; i++) {
351     uuid_t checkid;
352     noit_check_t *check;
353     xmlNodePtr node;
354     char *uuid_conf;
355
356     node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
357     uuid_conf = (char *)xmlGetProp(node, (xmlChar *)"uuid");
358     if(!uuid_conf || uuid_parse(uuid_conf, checkid)) {
359       nc_printf(ncct, "%s has invalid or missing UUID!\n",
360                 (char *)xmlGetNodePath(node) + strlen("/noit"));
361       continue;
362     }
363     if(period == 0) {
364       check = noit_poller_lookup(checkid);
365       if(!check) continue;
366       if(adding) noit_check_transient_add_feed(check, ncct->feed_path);
367       else noit_check_transient_remove_feed(check, ncct->feed_path);
368     }
369     else {
370       if(adding) {
371         check = noit_check_watch(checkid, period);
372         /* This check must be watched from the console */
373         noit_check_transient_add_feed(check, ncct->feed_path);
374         /* Note the check */
375         noit_check_log_check(check);
376         /* kick it off, if it isn't running already */
377         if(!NOIT_CHECK_LIVE(check)) noit_check_activate(check);
378       }
379       else {
380         check = noit_check_get_watch(checkid, period);
381         if(check) noit_check_transient_remove_feed(check, ncct->feed_path);
382       }
383     }
384   }
385  out:
386   if(pobj) xmlXPathFreeObject(pobj);
387   return 0;
388 }
389 static int
390 noit_console_show_check(noit_console_closure_t ncct,
391                         int argc, char **argv,
392                         noit_console_state_t *state, void *closure) {
393   int i, cnt;
394   noit_conf_t_userdata_t *info;
395   char xpath[1024];
396   xmlXPathObjectPtr pobj = NULL;
397   xmlXPathContextPtr xpath_ctxt = NULL;
398
399   noit_conf_xml_xpath(NULL, &xpath_ctxt);
400   if(argc > 1) {
401     nc_printf(ncct, "requires zero or one arguments\n");
402     return -1;
403   }
404
405   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
406   /* We many not be in conf-t mode -- that's fine */
407   if(noit_console_mkcheck_xpath(xpath, sizeof(xpath), info,
408                                 argc ? argv[0] : NULL)) {
409     nc_printf(ncct, "could not find check '%s'\n", argv[0]);
410     return -1;
411   }
412
413   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
414   if(!pobj || pobj->type != XPATH_NODESET ||
415      xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
416     nc_printf(ncct, "no checks found\n");
417     goto out;
418   }
419   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
420   if(info && cnt != 1) {
421     nc_printf(ncct, "Ambiguous check specified\n");
422     goto out;
423   }
424   for(i=0; i<cnt; i++) {
425     noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
426     const char *k;
427     int klen;
428     void *data;
429     uuid_t checkid;
430     noit_check_t *check;
431     noit_hash_table *config;
432     xmlNodePtr node, anode, mnode = NULL;
433     char *uuid_conf;
434     char *module, *value;
435
436     node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
437     uuid_conf = (char *)xmlGetProp(node, (xmlChar *)"uuid");
438     if(!uuid_conf || uuid_parse(uuid_conf, checkid)) {
439       nc_printf(ncct, "%s has invalid or missing UUID!\n",
440                 (char *)xmlGetNodePath(node) + strlen("/noit"));
441       continue;
442     }
443     nc_printf(ncct, "==== %s ====\n", uuid_conf);
444
445 #define MYATTR(a,n,b) _noit_conf_get_string(node, &(n), "@" #a, &(b))
446 #define INHERIT(a,n,b) \
447   _noit_conf_get_string(node, &(n), "ancestor-or-self::node()/@" #a, &(b))
448 #define SHOW_ATTR(a) do { \
449   anode = NULL; \
450   value = NULL; \
451   INHERIT(a, anode, value); \
452   nc_attr_show(ncct, #a, node, anode, value); \
453 } while(0)
454
455     if(!INHERIT(module, mnode, module)) module = NULL;
456     if(MYATTR(name, anode, value))
457       nc_printf(ncct, " name: %s\n", value);
458     else
459       nc_printf(ncct, " name: %s [from module]\n", module ? module : "[undef]");
460     nc_attr_show(ncct, "module", node, mnode, module);
461     SHOW_ATTR(target);
462     SHOW_ATTR(period);
463     SHOW_ATTR(timeout);
464     SHOW_ATTR(oncheck);
465     SHOW_ATTR(filterset);
466     SHOW_ATTR(disable);
467     /* Print out all the config settings */
468     config = noit_conf_get_hash(node, "config");
469     while(noit_hash_next(config, &iter, &k, &klen, &data)) {
470       nc_printf(ncct, " config::%s: %s\n", k, (const char *)data);
471     }
472     noit_hash_destroy(config, free, free);
473     free(config);
474
475     check = noit_poller_lookup(checkid);
476     if(!check) {
477       nc_printf(ncct, " ERROR: not in running system\n");
478     }
479     else {
480       int idx = 0;
481       nc_printf(ncct, " currently: ");
482       if(NOIT_CHECK_RUNNING(check)) nc_printf(ncct, "%srunning", idx++?",":"");
483       if(NOIT_CHECK_KILLED(check)) nc_printf(ncct, "%skilled", idx++?",":"");
484       if(!NOIT_CHECK_CONFIGURED(check)) nc_printf(ncct, "%sunconfig", idx++?",":"");
485       if(NOIT_CHECK_DISABLED(check)) nc_printf(ncct, "%sdisabled", idx++?",":"");
486       if(!idx) nc_printf(ncct, "idle");
487       nc_write(ncct, "\n", 1);
488       if(check->stats.current.whence.tv_sec == 0) {
489         nc_printf(ncct, " last run: never\n");
490       }
491       else {
492         stats_t *c = &check->stats.current;
493         struct timeval now, diff;
494         gettimeofday(&now, NULL);
495         sub_timeval(now, c->whence, &diff);
496         nc_printf(ncct, " last run: %0.3f seconds ago\n",
497                   diff.tv_sec + (diff.tv_usec / 1000000.0));
498         nc_printf(ncct, " availability/state: %s/%s\n",
499                   noit_check_available_string(c->available),
500                   noit_check_state_string(c->state));
501         nc_printf(ncct, " status: %s\n", c->status ? c->status : "[[null]]");
502         nc_printf(ncct, " metrics:\n");
503         memset(&iter, 0, sizeof(iter));
504         while(noit_hash_next(&c->metrics, &iter, &k, &klen, &data)) {
505           char buff[256];
506           noit_stats_snprint_metric(buff, sizeof(buff), (metric_t *)data);
507           nc_printf(ncct, "   %s\n", buff);
508         }
509       }
510     }
511   }
512  out:
513   if(pobj) xmlXPathFreeObject(pobj);
514   return 0;
515 }
516 static int
517 noit_console_config_nocheck(noit_console_closure_t ncct,
518                             int argc, char **argv,
519                             noit_console_state_t *state, void *closure) {
520   int i, cnt;
521   const char *err = "internal error";
522   noit_conf_t_userdata_t *info;
523   xmlXPathObjectPtr pobj = NULL;
524   xmlXPathContextPtr xpath_ctxt = NULL;
525   char xpath[1024];
526   uuid_t checkid;
527
528   noit_conf_xml_xpath(NULL, &xpath_ctxt);
529   if(argc < 1) {
530     nc_printf(ncct, "requires one argument\n");
531     return -1;
532   }
533
534   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
535   if(noit_console_mkcheck_xpath(xpath, sizeof(xpath), info, argv[0])) {
536     nc_printf(ncct, "could not find check '%s'\n", argv[0]);
537     return -1;
538   }
539   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
540   if(!pobj || pobj->type != XPATH_NODESET ||
541      xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
542     err = "no checks found";
543     goto bad;
544   }
545   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
546   for(i=0; i<cnt; i++) {
547     xmlNodePtr node;
548     char *uuid_conf;
549     node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
550     uuid_conf = (char *)xmlGetProp(node, (xmlChar *)"uuid");
551     if(!uuid_conf || uuid_parse(uuid_conf, checkid)) {
552       nc_printf(ncct, "%s has invalid or missing UUID!\n",
553                 (char *)xmlGetNodePath(node) + strlen("/noit"));
554     }
555     else {
556       if(argc > 1) {
557         int j;
558         for(j=1;j<argc;j++)
559           xmlUnsetProp(node, (xmlChar *)argv[j]);
560       } else {
561         nc_printf(ncct, "descheduling %s\n", uuid_conf);
562         noit_poller_deschedule(checkid);
563         xmlUnlinkNode(node);
564       }
565       noit_conf_mark_changed();
566     }
567   }
568   if(argc > 1) {
569     noit_poller_process_checks(xpath);
570     noit_poller_reload(xpath);
571   }
572   nc_printf(ncct, "rebuilding causal map...\n");
573   noit_poller_make_causal_map();
574   if(pobj) xmlXPathFreeObject(pobj);
575   return 0;
576  bad:
577   if(pobj) xmlXPathFreeObject(pobj);
578   nc_printf(ncct, "%s\n", err);
579   return -1;
580 }
581 static int
582 noit_console_state_conf_terminal(noit_console_closure_t ncct,
583                                  int argc, char **argv,
584                                  noit_console_state_t *state, void *closure) {
585   noit_conf_t_userdata_t *info;
586   if(argc) {
587     nc_printf(ncct, "extra arguments not expected.\n");
588     return -1;
589   }
590   info = calloc(1, sizeof(*info));
591   info->path = strdup("/");
592   noit_console_userdata_set(ncct, NOIT_CONF_T_USERDATA, info,
593                             conf_t_userdata_free);
594   noit_console_state_push_state(ncct, state);
595   noit_console_state_init(ncct);
596   return 0;
597 }
598 static int
599 noit_console_config_section(noit_console_closure_t ncct,
600                             int argc, char **argv,
601                             noit_console_state_t *state, void *closure) {
602   const char *err = "internal error";
603   char *path, xpath[1024];
604   noit_conf_t_userdata_t *info;
605   xmlXPathObjectPtr pobj = NULL;
606   xmlXPathContextPtr xpath_ctxt = NULL;
607   xmlNodePtr node = NULL, newnode;
608   vpsized_int delete = (vpsized_int)closure;
609
610   noit_conf_xml_xpath(NULL, &xpath_ctxt);
611   if(argc != 1) {
612     nc_printf(ncct, "requires one argument\n");
613     return -1;
614   }
615   if(strchr(argv[0], '/')) {
616     nc_printf(ncct, "invalid section name\n");
617     return -1;
618   }
619   if(!strcmp(argv[0], "check")) {
620     nc_printf(ncct, "use 'check' to create checks\n");
621     return -1;
622   }
623   if(!strcmp(argv[0], "config")) {
624     nc_printf(ncct, "use 'config' to set check config options\n");
625     return -1;
626   }
627   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
628   if(!strcmp(info->path, "/")) {
629     nc_printf(ncct, "manipulation of toplevel section disallowed\n");
630     return -1;
631   }
632
633   if(delete) {
634     /* We cannot delete if we have checks */
635     snprintf(xpath, sizeof(xpath), "/noit%s/%s//check", info->path, argv[0]);
636     pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
637     if(!pobj || pobj->type != XPATH_NODESET ||
638        !xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
639       err = "cannot delete section, has checks";
640       goto bad;
641     }
642     if(pobj) xmlXPathFreeObject(pobj);
643   }
644
645   snprintf(xpath, sizeof(xpath), "/noit%s/%s", info->path, argv[0]);
646   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
647   if(!pobj || pobj->type != XPATH_NODESET) {
648     err = "internal error: cannot detect section";
649     goto bad;
650   }
651   if(!delete && !xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
652     if(xmlXPathNodeSetGetLength(pobj->nodesetval) == 1) {
653       node = xmlXPathNodeSetItem(pobj->nodesetval, 0);
654       if(info->path) free(info->path);
655       info->path = strdup((char *)xmlGetNodePath(node) + strlen("/noit"));
656       goto cdout;
657     }
658     err = "cannot create section";
659     goto bad;
660   }
661   if(delete && xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
662     err = "no such section";
663     goto bad;
664   }
665   if(delete) {
666     node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
667     xmlUnlinkNode(node);
668     noit_conf_mark_changed();
669     return 0;
670   }
671   if(pobj) xmlXPathFreeObject(pobj);
672
673   path = strcmp(info->path, "/") ? info->path : "";
674   snprintf(xpath, sizeof(xpath), "/noit%s", path);
675   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
676   if(!pobj || pobj->type != XPATH_NODESET ||
677      xmlXPathNodeSetGetLength(pobj->nodesetval) != 1) {
678     err = "path invalid?";
679     goto bad;
680   }
681   node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
682   if((newnode = xmlNewChild(node, NULL, (xmlChar *)argv[0], NULL)) != NULL) {
683     noit_conf_mark_changed();
684     if(info->path) free(info->path);
685     info->path = strdup((char *)xmlGetNodePath(newnode) + strlen("/noit"));
686   }
687   else {
688     err = "failed to create section";
689     goto bad;
690   }
691  cdout:
692   if(pobj) xmlXPathFreeObject(pobj);
693   return 0;
694  bad:
695   if(pobj) xmlXPathFreeObject(pobj);
696   nc_printf(ncct, "%s\n", err);
697   return -1;
698 }
699
700 static int
701 noit_console_config_cd(noit_console_closure_t ncct,
702                        int argc, char **argv,
703                        noit_console_state_t *state, void *closure) {
704   const char *err = "internal error";
705   char *path, xpath[1024];
706   noit_conf_t_userdata_t *info;
707   xmlXPathObjectPtr pobj = NULL;
708   xmlXPathContextPtr xpath_ctxt = NULL, current_ctxt;
709   xmlNodePtr node = NULL;
710   char *dest;
711
712   noit_conf_xml_xpath(NULL, &xpath_ctxt);
713   if(argc != 1 && !closure) {
714     nc_printf(ncct, "requires one argument\n");
715     return -1;
716   }
717   dest = argc ? argv[0] : (char *)closure;
718   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
719   if(dest[0] == '/')
720     snprintf(xpath, sizeof(xpath), "/noit%s", dest);
721   else {
722     snprintf(xpath, sizeof(xpath), "/noit%s/%s", info->path, dest);
723   }
724   if(xpath[strlen(xpath)-1] == '/') xpath[strlen(xpath)-1] = '\0';
725
726   current_ctxt = xpath_ctxt;
727   pobj = xmlXPathEval((xmlChar *)xpath, current_ctxt);
728   if(!pobj || pobj->type != XPATH_NODESET ||
729      xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
730     err = "no such section";
731     goto bad;
732   }
733   if(xmlXPathNodeSetGetLength(pobj->nodesetval) > 1) {
734     err = "ambiguous section";
735     goto bad;
736   }
737
738   node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
739   if(!strcmp((char *)node->name, "check")) {
740     err = "can't cd into a check, use 'check' instead";
741     goto bad;
742   }
743   path = (char *)xmlGetNodePath(node);
744   if(strncmp(path, "/noit/", strlen("/noit/")) && strcmp(path, "/noit")) {
745     err = "new path outside out tree";
746     goto bad;
747   }
748   free(info->path);
749   if(!strcmp(path, "/noit"))
750     info->path = strdup("/");
751   else
752     info->path = strdup((char *)xmlGetNodePath(node) + strlen("/noit"));
753   if(pobj) xmlXPathFreeObject(pobj);
754   if(closure) noit_console_state_pop(ncct, argc, argv, NULL, NULL);
755   return 0;
756  bad:
757   if(pobj) xmlXPathFreeObject(pobj);
758   nc_printf(ncct, "%s [%s]\n", err, xpath);
759   return -1;
760 }
761 static int
762 noit_console_config_show(noit_console_closure_t ncct,
763                          int argc, char **argv,
764                          noit_console_state_t *state, void *closure) {
765   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
766   const char *k;
767   int klen;
768   void *data;
769   int i, cnt, titled = 0, cliplen = 0;
770   const char *path = "", *basepath = NULL;
771   char xpath[1024];
772   noit_conf_t_userdata_t *info = NULL;
773   noit_hash_table *config;
774   xmlXPathObjectPtr pobj = NULL;
775   xmlXPathContextPtr xpath_ctxt = NULL, current_ctxt;
776   xmlDocPtr master_config = NULL;
777   xmlNodePtr node = NULL;
778
779   noit_conf_xml_xpath(&master_config, &xpath_ctxt);
780   if(argc > 1) {
781     nc_printf(ncct, "too many arguments\n");
782     return -1;
783   }
784
785   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
786   if(info) path = basepath = info->path;
787   if(!info && argc == 0) {
788     nc_printf(ncct, "argument required when not in configuration mode\n");
789     return -1;
790   }
791
792   if(argc == 1) path = argv[0];
793   if(!basepath) basepath = path;
794
795   /* { / } is a special case */
796   if(!strcmp(basepath, "/")) basepath = "";
797   if(!strcmp(path, "/")) path = "";
798
799   if(!master_config) {
800     nc_printf(ncct, "no config\n");
801     return -1;
802   }
803
804   /* { / } is the only path that will end with a /
805    * in XPath { / / * } means something _entirely different than { / * }
806    * Ever notice how it is hard to describe xpath in C comments?
807    */
808   /* We don't want to show the root node */
809   cliplen = strlen("/noit/");
810
811   /* If we are in configuration mode
812    * and we are without an argument or the argument is absolute,
813    * clip the current path off */
814   if(info && (argc == 0 || path[0] != '/')) cliplen += strlen(basepath);
815   if(!path[0] || path[0] == '/') /* base only, or absolute path requested */
816     snprintf(xpath, sizeof(xpath), "/noit%s/@*", path);
817   else
818     snprintf(xpath, sizeof(xpath), "/noit%s/%s/@*", basepath, path);
819
820   current_ctxt = xpath_ctxt;
821   pobj = xmlXPathEval((xmlChar *)xpath, current_ctxt);
822   if(!pobj || pobj->type != XPATH_NODESET) {
823     nc_printf(ncct, "no such object\n");
824     goto bad;
825   }
826   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
827   titled = 0;
828   for(i=0; i<cnt; i++) {
829     node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
830     if(!strcmp((char *)node->name, "check")) continue;
831     if(node->children && node->children == xmlGetLastChild(node) &&
832       xmlNodeIsText(node->children)) {
833       if(!titled++) nc_printf(ncct, "== Section Settings ==\n");
834       nc_printf(ncct, "%s: %s\n", xmlGetNodePath(node) + cliplen,
835                 xmlXPathCastNodeToString(node->children));
836     }
837   }
838   xmlXPathFreeObject(pobj);
839
840   /* Print out all the config settings */
841   if(!path[0] || path[0] == '/') /* base only, or absolute path requested */
842     snprintf(xpath, sizeof(xpath), "/noit%s", path);
843   else
844     snprintf(xpath, sizeof(xpath), "/noit%s/%s", basepath, path);
845   pobj = xmlXPathEval((xmlChar *)xpath, current_ctxt);
846   if(!pobj || pobj->type != XPATH_NODESET) {
847     nc_printf(ncct, "no such object\n");
848     goto bad;
849   }
850   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
851   node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
852   titled = 0;
853   config = noit_conf_get_hash(node, "config");
854   while(noit_hash_next(config, &iter, &k, &klen, &data)) {
855     if(!titled++) nc_printf(ncct, "== Section [Aggregated] Config ==\n");
856     nc_printf(ncct, "config::%s: %s\n", k, (const char *)data);
857   }
858   noit_hash_destroy(config, free, free);
859   free(config);
860   xmlXPathFreeObject(pobj);
861
862   /* _shorten string_ turning last { / @ * } to { / * } */
863   if(!path[0] || path[0] == '/') /* base only, or absolute path requested */
864     snprintf(xpath, sizeof(xpath), "/noit%s/*", path);
865   else
866     snprintf(xpath, sizeof(xpath), "/noit%s/%s/*", basepath, path);
867   pobj = xmlXPathEval((xmlChar *)xpath, current_ctxt);
868   if(!pobj || pobj->type != XPATH_NODESET) {
869     nc_printf(ncct, "no such object\n");
870     goto bad;
871   }
872   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
873   titled = 0;
874   for(i=0; i<cnt; i++) {
875     node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
876     if(!strcmp((char *)node->name, "check")) continue;
877     if(!strcmp((char *)xmlGetNodePath(node) + cliplen, "config")) continue;
878     if(!(node->children && node->children == xmlGetLastChild(node) &&
879          xmlNodeIsText(node->children))) {
880       if(!titled++) nc_printf(ncct, "== Subsections ==\n");
881       nc_printf(ncct, "%s\n", xmlGetNodePath(node) + cliplen);
882     }
883   }
884
885   titled = 0;
886   for(i=0; i<cnt; i++) {
887     node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
888     if(!strcmp((char *)node->name, "check")) {
889       int busted = 1;
890       xmlAttr *attr;
891       char *uuid_str = "undefined";
892
893       if(!titled++) nc_printf(ncct, "== Checks ==\n");
894
895       for(attr=node->properties; attr; attr = attr->next) {
896         if(!strcmp((char *)attr->name, "uuid"))
897           uuid_str = (char *)xmlXPathCastNodeToString(attr->children);
898       }
899       if(uuid_str) {
900         uuid_t checkid;
901         nc_printf(ncct, "check[@uuid=\"%s\"] ", uuid_str);
902         if(uuid_parse(uuid_str, checkid) == 0) {
903           noit_check_t *check;
904           check = noit_poller_lookup(checkid);
905           if(check) {
906             busted = 0;
907             nc_printf(ncct, "%s`%s`%s", check->target, check->module, check->name);
908           }
909         }
910       }
911       else
912         nc_printf(ncct, "%s ", xmlGetNodePath(node) + cliplen);
913       if(busted) nc_printf(ncct, "[check not in running system]");
914       nc_write(ncct, "\n", 1);
915     }
916   }
917   xmlXPathFreeObject(pobj);
918   return 0;
919  bad:
920   if(pobj) xmlXPathFreeObject(pobj);
921   return -1;
922 }
923
924 static char *
925 conf_t_check_prompt(EditLine *el) {
926   noit_console_closure_t ncct;
927   noit_conf_t_userdata_t *info;
928   noit_check_t *check;
929   static char *tl = "noit(conf)# ";
930   static char *pfmt = "noit(conf:%s%s%s)# ";
931
932   el_get(el, EL_USERDATA, (void *)&ncct);
933   if(!ncct) return tl;
934   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
935   if(!info) return tl;
936
937   check = noit_poller_lookup(info->current_check);
938   if(check &&
939      check->target && check->target[0] &&
940      check->name && check->name[0])
941     snprintf(info->prompt, sizeof(info->prompt),
942              pfmt, check->target, "`", check->name);
943   else {
944     char uuid_str[37];
945     uuid_unparse_lower(info->current_check, uuid_str);
946     snprintf(info->prompt, sizeof(info->prompt), pfmt, "[", uuid_str, "]");
947   }
948   return info->prompt;
949 }
950 static char *
951 conf_t_prompt(EditLine *el) {
952   noit_console_closure_t ncct;
953   noit_conf_t_userdata_t *info;
954   static char *tl = "noit(conf)# ";
955   static char *pfmt = "noit(conf:%s%s)# ";
956   int path_len, max_len;
957
958   el_get(el, EL_USERDATA, (void *)&ncct);
959   if(!ncct) return tl;
960   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
961   if(!info) return tl;
962
963   path_len = strlen(info->path);
964   max_len = sizeof(info->prompt) - (strlen(pfmt) - 4 /* %s%s */) - 1 /* \0 */;
965   if(path_len > max_len)
966     snprintf(info->prompt, sizeof(info->prompt),
967              pfmt, "...", info->path + path_len - max_len + 3 /* ... */);
968   else
969     snprintf(info->prompt, sizeof(info->prompt), pfmt, "", info->path);
970   return info->prompt;
971 }
972 static int
973 noit_conf_checks_reload(noit_console_closure_t ncct,
974                         int argc, char **argv,
975                         noit_console_state_t *state, void *closure) {
976   if(noit_conf_reload(ncct, argc, argv, state, closure)) return -1;
977   noit_poller_reload(NULL);
978   return 0;
979 }
980
981 static int
982 validate_attr_set_scope(noit_conf_t_userdata_t *info,
983                         struct _valid_attr_t *attrinfo) {
984   int len;
985   len = strlen(attrinfo->scope);
986   if(strncmp(info->path, attrinfo->scope, len) ||
987      (info->path[len] != '\0' && info->path[len] != '/')) {
988     return -1;
989   }
990   return 0;
991 }
992 static int
993 replace_config(noit_console_closure_t ncct,
994                noit_conf_t_userdata_t *info, const char *name,
995                const char *value) {
996   int i, cnt, rv = -1, active = 0;
997   xmlXPathObjectPtr pobj = NULL;
998   xmlXPathContextPtr xpath_ctxt = NULL;
999   xmlNodePtr node, confignode;
1000   char xpath[1024], *path;
1001
1002   path = info->path;
1003   if(!strcmp(path, "/")) path = "";
1004
1005   noit_conf_xml_xpath(NULL, &xpath_ctxt);
1006
1007   /* Only if checks will fixate this attribute shall we check for
1008    * child <check> nodes.
1009    * NOTE: this return nothing and "seems" okay if we are _in_
1010    *       a <check> node.  That case is handled below.
1011    */
1012   snprintf(xpath, sizeof(xpath), "/noit/%s//check[@uuid]", path);
1013   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
1014   if(!pobj || pobj->type != XPATH_NODESET) goto out;
1015   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
1016   for(i=0; i<cnt; i++) {
1017     uuid_t checkid;
1018     node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
1019     if(noit_conf_get_uuid(node, "@uuid", checkid)) {
1020       noit_check_t *check;
1021       check = noit_poller_lookup(checkid);
1022       if(check && NOIT_CHECK_LIVE(check)) active++;
1023     }
1024   }
1025   if(pobj) xmlXPathFreeObject(pobj);
1026
1027   snprintf(xpath, sizeof(xpath), "/noit/%s", path);
1028   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
1029   if(!pobj || pobj->type != XPATH_NODESET) goto out;
1030   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
1031   if(cnt != 1) {
1032     nc_printf(ncct, "Internal error: context node disappeared\n");
1033     goto out;
1034   }
1035   node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
1036   if(strcmp((const char *)node->name, "check")) {
1037     uuid_t checkid;
1038     /* Detect if  we are actually a <check> node and attempting to
1039      * change something we shouldn't.
1040      * This is the counterpart noted above.
1041      */
1042     if(noit_conf_get_uuid(node, "@uuid", checkid)) {
1043       noit_check_t *check;
1044       check = noit_poller_lookup(checkid);
1045       if(NOIT_CHECK_LIVE(check)) active++;
1046     }
1047   }
1048 #ifdef UNSAFE_RECONFIG
1049   if(active) {
1050     nc_printf(ncct, "Cannot set '%s', it would effect %d live check(s)\n",
1051               name, active);
1052     goto out;
1053   }
1054 #endif
1055   if(pobj) xmlXPathFreeObject(pobj);
1056
1057   /* Here we want to remove /noit/path/config/name */
1058   snprintf(xpath, sizeof(xpath), "/noit/%s/config/%s", path, name);
1059   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
1060   if(!pobj || pobj->type != XPATH_NODESET) goto out;
1061   if(xmlXPathNodeSetGetLength(pobj->nodesetval) > 0) {
1062     xmlNodePtr toremove;
1063     toremove = xmlXPathNodeSetItem(pobj->nodesetval, 0);
1064     xmlUnlinkNode(toremove);
1065   }
1066   /* TODO: if there are no more children of config, remove config? */
1067   if(value) {
1068     if(pobj) xmlXPathFreeObject(pobj);
1069     /* He we create config if needed and place a child node under it */
1070     snprintf(xpath, sizeof(xpath), "/noit/%s/config", path);
1071     pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
1072     if(!pobj || pobj->type != XPATH_NODESET) goto out;
1073     if(xmlXPathNodeSetGetLength(pobj->nodesetval) == 0) {
1074       if(pobj) xmlXPathFreeObject(pobj);
1075       snprintf(xpath, sizeof(xpath), "/noit/%s", path);
1076       pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
1077       if(!pobj || pobj->type != XPATH_NODESET) goto out;
1078       if(xmlXPathNodeSetGetLength(pobj->nodesetval) != 1) {
1079         nc_printf(ncct, "Node disappeared from under you!\n");
1080         goto out;
1081       }
1082       confignode = xmlNewChild(xmlXPathNodeSetItem(pobj->nodesetval, 0),
1083                                NULL, (xmlChar *)"config", NULL);
1084       if(confignode == NULL) {
1085         nc_printf(ncct, "Error creating config child node.\n");
1086         goto out;
1087       }
1088     }
1089     else confignode = xmlXPathNodeSetItem(pobj->nodesetval, 0);
1090
1091     assert(confignode);
1092     /* Now we create a child */
1093     xmlNewChild(confignode, NULL, (xmlChar *)name, (xmlChar *)value);
1094   }
1095   noit_conf_mark_changed();
1096   rv = 0;
1097  out:
1098   if(pobj) xmlXPathFreeObject(pobj);
1099   return rv;
1100 }
1101 static int
1102 replace_attr(noit_console_closure_t ncct,
1103              noit_conf_t_userdata_t *info, struct _valid_attr_t *attrinfo,
1104              const char *value) {
1105   int i, cnt, rv = -1, active = 0;
1106   xmlXPathObjectPtr pobj = NULL;
1107   xmlXPathContextPtr xpath_ctxt = NULL;
1108   xmlNodePtr node;
1109   char xpath[1024], *path;
1110
1111   path = info->path;
1112   if(!strcmp(path, "/")) path = "";
1113
1114   noit_conf_xml_xpath(NULL, &xpath_ctxt);
1115   if(attrinfo->checks_fixate) {
1116     /* Only if checks will fixate this attribute shall we check for
1117      * child <check> nodes.
1118      * NOTE: this return nothing and "seems" okay if we are _in_
1119      *       a <check> node.  That case is handled below.
1120      */
1121     snprintf(xpath, sizeof(xpath), "/noit/%s//check[@uuid]", path);
1122     pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
1123     if(!pobj || pobj->type != XPATH_NODESET) goto out;
1124     cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
1125     for(i=0; i<cnt; i++) {
1126       uuid_t checkid;
1127       node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
1128       if(noit_conf_get_uuid(node, "@uuid", checkid)) {
1129         noit_check_t *check;
1130         check = noit_poller_lookup(checkid);
1131         if(check && NOIT_CHECK_LIVE(check)) active++;
1132       }
1133     }
1134     if(pobj) xmlXPathFreeObject(pobj);
1135   }
1136   snprintf(xpath, sizeof(xpath), "/noit/%s", path);
1137   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
1138   if(!pobj || pobj->type != XPATH_NODESET) goto out;
1139   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
1140   if(cnt != 1) {
1141     nc_printf(ncct, "Internal error: context node disappeared\n");
1142     goto out;
1143   }
1144   node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
1145   if(attrinfo->checks_fixate &&
1146      !strcmp((const char *)node->name, "check")) {
1147     uuid_t checkid;
1148     /* Detect if  we are actually a <check> node and attempting to
1149      * change something we shouldn't.
1150      * This is the counterpart noted above.
1151      */
1152     if(noit_conf_get_uuid(node, "@uuid", checkid)) {
1153       noit_check_t *check;
1154       check = noit_poller_lookup(checkid);
1155       if(check && NOIT_CHECK_LIVE(check)) active++;
1156     }
1157   }
1158   if(active) {
1159     nc_printf(ncct, "Cannot set '%s', it would effect %d live check(s)\n",
1160               attrinfo->name, active);
1161     goto out;
1162   }
1163   xmlUnsetProp(node, (xmlChar *)attrinfo->name);
1164   if(value)
1165     xmlSetProp(node, (xmlChar *)attrinfo->name, (xmlChar *)value);
1166   noit_conf_mark_changed();
1167   rv = 0;
1168  out:
1169   if(pobj) xmlXPathFreeObject(pobj);
1170   return rv;
1171 }
1172 int
1173 noit_conf_check_set_attr(noit_console_closure_t ncct,
1174                          int argc, char **argv,
1175                          noit_console_state_t *state, void *closure) {
1176   struct _valid_attr_t *attrinfo = closure;
1177   noit_conf_t_userdata_t *info;
1178
1179   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
1180   if(!info || validate_attr_set_scope(info, attrinfo)) {
1181     nc_printf(ncct, "'%s' attribute only valid in %s scope\n",
1182               attrinfo->name, attrinfo->scope);
1183     return -1;
1184   }
1185
1186   if(argc != 1) {
1187     nc_printf(ncct, "set requires exactly one value\n");
1188     return -1;
1189   }
1190   /* Okay, we have an attribute and it should be set/replaced on the
1191    * current path.
1192    */
1193   if(replace_attr(ncct, info, attrinfo, argv[0])) {
1194     return -1;
1195   }
1196
1197   /* So, we updated an attribute, so we need to reload all checks
1198    * that are descendent-or-self of this node.
1199    */
1200   if(!strncmp(info->path, "/checks", strlen("/checks")))
1201     refresh_subchecks(ncct, info);
1202   if(!strncmp(info->path, "/filtersets", strlen("/filtersets")))
1203     noit_refresh_filtersets(ncct, info);
1204   return 0;
1205 }
1206
1207 int
1208 noit_conf_check_unset_attr(noit_console_closure_t ncct,
1209                            int argc, char **argv,
1210                            noit_console_state_t *state, void *closure) {
1211   struct _valid_attr_t *attrinfo = closure;
1212   noit_conf_t_userdata_t *info;
1213
1214   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
1215   if(!info || validate_attr_set_scope(info, attrinfo)) {
1216     nc_printf(ncct, "'%s' attribute only valid in %s scope\n",
1217               attrinfo->name, attrinfo->scope);
1218     return -1;
1219   }
1220
1221   if(argc != 0) {
1222     nc_printf(ncct, "no arguments allowed to this command.\n");
1223     return -1;
1224   }
1225   /* Okay, we have an attribute and it should be set/replaced on the
1226    * current path.
1227    */
1228   if(replace_attr(ncct, info, attrinfo, NULL)) {
1229     return -1;
1230   }
1231
1232   /* So, we updated an attribute, so we need to reload all checks
1233    * that are descendent-or-self of this node.
1234    */
1235   if(!strncmp(info->path, "/checks", strlen("/checks")))
1236     refresh_subchecks(ncct, info);
1237   if(!strncmp(info->path, "/filterset", strlen("/filterest")))
1238     noit_refresh_filtersets(ncct, info);
1239   return 0;
1240 }
1241
1242 int
1243 noit_console_config_setconfig(noit_console_closure_t ncct,
1244                                 int argc, char **argv,
1245                                 noit_console_state_t *state, void *closure) {
1246   noit_conf_t_userdata_t *info;
1247
1248   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
1249
1250   if(argc != 2) {
1251     nc_printf(ncct, "two arguments required.\n");
1252     return -1;
1253   }
1254   /* Okay, we have an child name and it should be culled from
1255    * current path/config.
1256    */
1257   if(replace_config(ncct, info, argv[0], argv[1])) {
1258     return -1;
1259   }
1260
1261   /* So, we updated an attribute, so we need to reload all checks
1262    * that are descendent-or-self of this node.
1263    */
1264   refresh_subchecks(ncct, info);
1265   return 0;
1266 }
1267
1268 int
1269 noit_console_config_unsetconfig(noit_console_closure_t ncct,
1270                                 int argc, char **argv,
1271                                 noit_console_state_t *state, void *closure) {
1272   noit_conf_t_userdata_t *info;
1273
1274   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
1275
1276   if(argc != 1) {
1277     nc_printf(ncct, "one argument required.\n");
1278     return -1;
1279   }
1280   /* Okay, we have an child name and it should be culled from
1281    * current path/config.
1282    */
1283   if(replace_config(ncct, info, argv[0], NULL)) {
1284     return -1;
1285   }
1286
1287   /* So, we updated an attribute, so we need to reload all checks
1288    * that are descendent-or-self of this node.
1289    */
1290   refresh_subchecks(ncct, info);
1291   return 0;
1292 }
1293
1294
1295 #define NEW_STATE(a) (a) = noit_console_state_alloc()
1296 #define ADD_CMD(a,cmd,func,ac,ss,c) \
1297   noit_console_state_add_cmd((a), \
1298     NCSCMD(cmd, func, ac, ss, c))
1299 #define DELEGATE_CMD(a,cmd,ac,ss) \
1300   noit_console_state_add_cmd((a), \
1301     NCSCMD(cmd, noit_console_state_delegate, ac, ss, NULL))
1302
1303 static
1304 void register_console_config_commands() {
1305   cmd_info_t *showcmd, *nocmd;
1306   noit_console_state_t *tl, *_conf_state, *_conf_t_state,
1307                        *_conf_t_check_state,
1308                        *_write_state, *_attr_state,
1309                        *_unset_state, *_uattr_state;
1310
1311   tl = noit_console_state_initial();
1312
1313   /* write <terimal|memory|file> */
1314   NEW_STATE(_write_state);
1315   ADD_CMD(_write_state, "terminal", noit_conf_write_terminal, NULL, NULL, NULL);
1316   ADD_CMD(_write_state, "file", noit_conf_write_file, NULL, NULL, NULL);
1317   /* write memory?  It's to a file, but I like router syntax */
1318   ADD_CMD(_write_state, "memory", noit_conf_write_file, NULL, NULL, NULL);
1319
1320   /* attribute <attrname> <value> */
1321   NEW_STATE(_attr_state);
1322   noit_console_state_add_check_attrs(_attr_state, noit_conf_check_set_attr);
1323  
1324   /* no attribute <attrname> <value> */
1325   NEW_STATE(_uattr_state);
1326   noit_console_state_add_check_attrs(_uattr_state, noit_conf_check_unset_attr);
1327
1328   NEW_STATE(_unset_state);
1329   DELEGATE_CMD(_unset_state, "attribute",
1330                noit_console_opt_delegate, _uattr_state);
1331   ADD_CMD(_unset_state, "section",
1332           noit_console_config_section, NULL, NULL, (void *)1);
1333   ADD_CMD(_unset_state, "config",
1334           noit_console_config_unsetconfig, NULL, NULL, NULL);
1335   ADD_CMD(_unset_state, "check",
1336           noit_console_config_nocheck, NULL, NULL, NULL);
1337  
1338   NEW_STATE(_conf_t_check_state);
1339   _conf_t_check_state->console_prompt_function = conf_t_check_prompt;
1340   DELEGATE_CMD(_conf_t_check_state, "attribute",
1341                noit_console_opt_delegate, _attr_state);
1342   DELEGATE_CMD(_conf_t_check_state, "no",
1343                noit_console_opt_delegate, _unset_state);
1344   ADD_CMD(_conf_t_check_state, "config",
1345           noit_console_config_setconfig, NULL, NULL, NULL);
1346   ADD_CMD(_conf_t_check_state, "status",
1347           noit_console_show_check, NULL, NULL, NULL);
1348   ADD_CMD(_conf_t_check_state, "exit",
1349           noit_console_config_cd, NULL, NULL, "..");
1350   ADD_CMD(_conf_t_check_state, "check",
1351           noit_console_check, noit_console_conf_check_opts,
1352           _conf_t_check_state, "..");
1353
1354   NEW_STATE(_conf_t_state);
1355   _conf_t_state->console_prompt_function = conf_t_prompt;
1356   noit_console_state_add_cmd(_conf_t_state, &console_command_exit);
1357   ADD_CMD(_conf_t_state, "ls", noit_console_config_show, NULL, NULL, NULL);
1358   ADD_CMD(_conf_t_state, "cd", noit_console_config_cd, NULL, NULL, NULL);
1359   ADD_CMD(_conf_t_state, "config",
1360           noit_console_config_setconfig, NULL, NULL, NULL);
1361   ADD_CMD(_conf_t_state, "section",
1362           noit_console_config_section, NULL, NULL, (void *)0);
1363   ADD_CMD(_conf_t_state, "check",
1364           noit_console_check, noit_console_conf_check_opts,
1365           _conf_t_check_state, NULL);
1366
1367   showcmd = noit_console_state_get_cmd(tl, "show");
1368   ADD_CMD(showcmd->dstate, "check",
1369           noit_console_show_check, noit_console_check_opts, NULL, NULL);
1370
1371   ADD_CMD(tl, "watch",
1372           noit_console_watch_check, noit_console_check_opts, NULL, (void *)1);
1373
1374   nocmd = noit_console_state_get_cmd(tl, "no");
1375   ADD_CMD(nocmd->dstate, "watch",
1376           noit_console_watch_check, noit_console_check_opts, NULL, (void *)0);
1377
1378   DELEGATE_CMD(_conf_t_state, "write",
1379                noit_console_opt_delegate, _write_state);
1380   DELEGATE_CMD(_conf_t_state, "attribute",
1381                noit_console_opt_delegate, _attr_state);
1382   DELEGATE_CMD(_conf_t_state, "no", noit_console_opt_delegate, _unset_state);
1383
1384   NEW_STATE(_conf_state);
1385   ADD_CMD(_conf_state, "terminal",
1386           noit_console_state_conf_terminal, NULL, _conf_t_state, NULL);
1387
1388   ADD_CMD(tl, "configure",
1389           noit_console_state_delegate, noit_console_opt_delegate,
1390           _conf_state, NULL);
1391   ADD_CMD(tl, "write",
1392           noit_console_state_delegate, noit_console_opt_delegate,
1393           _write_state, NULL);
1394   ADD_CMD(tl, "reload", noit_conf_checks_reload, NULL, NULL, NULL);
1395 }
1396
Note: See TracBrowser for help on using the browser.