root/src/noit_conf_checks.c

Revision 70451c561661f38403852e3585f41976f6a42843, 41.8 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 6 years ago)

implements configuration pushing into the DB. refs #26

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