root/src/noit_conf_checks.c

Revision 2d2a01235fc4b87f4d1046e89762367af33f539d, 38.1 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 6 years ago)

update check configs on-the-fly. non-check configs are still sections which is ugly. (this now works within checks, but not sections). refs #15

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