root/src/noit_conf_checks.c

Revision 5e653fe60dcc517ff76c1f3bb82cee19b3c51fa5, 41.6 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 6 years ago)

fixes #30

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