root/src/noit_conf_checks.c

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

implements filter sets, fixes #24

  • 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.status)
409         nc_printf(ncct, " recently: %s\n", check->stats.current.status);
410     }
411   }
412  out:
413   if(pobj) xmlXPathFreeObject(pobj);
414   return 0;
415 }
416 static int
417 noit_console_config_nocheck(noit_console_closure_t ncct,
418                             int argc, char **argv,
419                             noit_console_state_t *state, void *closure) {
420   int i, cnt;
421   const char *err = "internal error";
422   noit_conf_t_userdata_t *info;
423   xmlXPathObjectPtr pobj = NULL;
424   xmlXPathContextPtr xpath_ctxt = NULL;
425   char xpath[1024];
426   uuid_t checkid;
427
428   noit_conf_xml_xpath(NULL, &xpath_ctxt);
429   if(argc < 1) {
430     nc_printf(ncct, "requires one argument\n");
431     return -1;
432   }
433
434   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
435   if(noit_console_mkcheck_xpath(xpath, sizeof(xpath), info, argv[0])) {
436     nc_printf(ncct, "could not find check '%s'\n", argv[0]);
437     return -1;
438   }
439   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
440   if(!pobj || pobj->type != XPATH_NODESET ||
441      xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
442     err = "no checks found";
443     goto bad;
444   }
445   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
446   for(i=0; i<cnt; i++) {
447     xmlNodePtr node;
448     char *uuid_conf;
449     node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
450     uuid_conf = (char *)xmlGetProp(node, (xmlChar *)"uuid");
451     if(!uuid_conf || uuid_parse(uuid_conf, checkid)) {
452       nc_printf(ncct, "%s has invalid or missing UUID!\n",
453                 (char *)xmlGetNodePath(node) + strlen("/noit"));
454     }
455     else {
456       if(argc > 1) {
457         int j;
458         for(j=1;j<argc;j++)
459           xmlUnsetProp(node, (xmlChar *)argv[j]);
460       } else {
461         nc_printf(ncct, "descheduling %s\n", uuid_conf);
462         noit_poller_deschedule(checkid);
463         xmlUnlinkNode(node);
464       }
465     }
466   }
467   if(argc > 1) {
468     noit_poller_process_checks(xpath);
469     noit_poller_reload(xpath);
470   }
471   nc_printf(ncct, "rebuilding causal map...\n");
472   noit_poller_make_causal_map();
473   if(pobj) xmlXPathFreeObject(pobj);
474   return 0;
475  bad:
476   if(pobj) xmlXPathFreeObject(pobj);
477   nc_printf(ncct, "%s\n", err);
478   return -1;
479 }
480 static int
481 noit_console_state_conf_terminal(noit_console_closure_t ncct,
482                                  int argc, char **argv,
483                                  noit_console_state_t *state, void *closure) {
484   noit_conf_t_userdata_t *info;
485   if(argc) {
486     nc_printf(ncct, "extra arguments not expected.\n");
487     return -1;
488   }
489   info = calloc(1, sizeof(*info));
490   info->path = strdup("/");
491   noit_console_userdata_set(ncct, NOIT_CONF_T_USERDATA, info,
492                             conf_t_userdata_free);
493   noit_console_state_push_state(ncct, state);
494   noit_console_state_init(ncct);
495   return 0;
496 }
497 static int
498 noit_console_config_section(noit_console_closure_t ncct,
499                             int argc, char **argv,
500                             noit_console_state_t *state, void *closure) {
501   const char *err = "internal error";
502   char *path, xpath[1024];
503   noit_conf_t_userdata_t *info;
504   xmlXPathObjectPtr pobj = NULL;
505   xmlXPathContextPtr xpath_ctxt = NULL;
506   xmlNodePtr node = NULL, newnode;
507   vpsized_int delete = (vpsized_int)closure;
508
509   noit_conf_xml_xpath(NULL, &xpath_ctxt);
510   if(argc != 1) {
511     nc_printf(ncct, "requires one argument\n");
512     return -1;
513   }
514   if(strchr(argv[0], '/')) {
515     nc_printf(ncct, "invalid section name\n");
516     return -1;
517   }
518   if(!strcmp(argv[0], "check")) {
519     nc_printf(ncct, "use 'check' to create checks\n");
520     return -1;
521   }
522   if(!strcmp(argv[0], "config")) {
523     nc_printf(ncct, "use 'config' to set check config options\n");
524     return -1;
525   }
526   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
527   if(!strcmp(info->path, "/")) {
528     nc_printf(ncct, "manipulation of toplevel section disallowed\n");
529     return -1;
530   }
531
532   if(delete) {
533     /* We cannot delete if we have checks */
534     snprintf(xpath, sizeof(xpath), "/noit%s/%s//check", info->path, argv[0]);
535     pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
536     if(!pobj || pobj->type != XPATH_NODESET ||
537        !xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
538       err = "cannot delete section, has checks";
539       goto bad;
540     }
541     if(pobj) xmlXPathFreeObject(pobj);
542   }
543
544   snprintf(xpath, sizeof(xpath), "/noit%s/%s", info->path, argv[0]);
545   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
546   if(!pobj || pobj->type != XPATH_NODESET) {
547     err = "internal error: cannot detect section";
548     goto bad;
549   }
550   if(!delete && !xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
551     if(xmlXPathNodeSetGetLength(pobj->nodesetval) == 1) {
552       node = xmlXPathNodeSetItem(pobj->nodesetval, 0);
553       if(info->path) free(info->path);
554       info->path = strdup((char *)xmlGetNodePath(node) + strlen("/noit"));
555       goto cdout;
556     }
557     err = "cannot create section";
558     goto bad;
559   }
560   if(delete && xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
561     err = "no such section";
562     goto bad;
563   }
564   if(delete) {
565     node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
566     xmlUnlinkNode(node);
567     return 0;
568   }
569   if(pobj) xmlXPathFreeObject(pobj);
570
571   path = strcmp(info->path, "/") ? info->path : "";
572   snprintf(xpath, sizeof(xpath), "/noit%s", path);
573   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
574   if(!pobj || pobj->type != XPATH_NODESET ||
575      xmlXPathNodeSetGetLength(pobj->nodesetval) != 1) {
576     err = "path invalid?";
577     goto bad;
578   }
579   node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
580   if((newnode = xmlNewChild(node, NULL, (xmlChar *)argv[0], NULL)) != NULL) {
581     if(info->path) free(info->path);
582     info->path = strdup((char *)xmlGetNodePath(newnode) + strlen("/noit"));
583   }
584   else {
585     err = "failed to create section";
586     goto bad;
587   }
588  cdout:
589   if(pobj) xmlXPathFreeObject(pobj);
590   return 0;
591  bad:
592   if(pobj) xmlXPathFreeObject(pobj);
593   nc_printf(ncct, "%s\n", err);
594   return -1;
595 }
596
597 static int
598 noit_console_config_cd(noit_console_closure_t ncct,
599                        int argc, char **argv,
600                        noit_console_state_t *state, void *closure) {
601   const char *err = "internal error";
602   char *path, xpath[1024];
603   noit_conf_t_userdata_t *info;
604   xmlXPathObjectPtr pobj = NULL;
605   xmlXPathContextPtr xpath_ctxt = NULL, current_ctxt;
606   xmlNodePtr node = NULL;
607   char *dest;
608
609   noit_conf_xml_xpath(NULL, &xpath_ctxt);
610   if(argc != 1 && !closure) {
611     nc_printf(ncct, "requires one argument\n");
612     return -1;
613   }
614   dest = argc ? argv[0] : (char *)closure;
615   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
616   if(dest[0] == '/')
617     snprintf(xpath, sizeof(xpath), "/noit%s", dest);
618   else {
619     snprintf(xpath, sizeof(xpath), "/noit%s/%s", info->path, dest);
620   }
621   if(xpath[strlen(xpath)-1] == '/') xpath[strlen(xpath)-1] = '\0';
622
623   current_ctxt = xpath_ctxt;
624   pobj = xmlXPathEval((xmlChar *)xpath, current_ctxt);
625   if(!pobj || pobj->type != XPATH_NODESET ||
626      xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
627     err = "no such section";
628     goto bad;
629   }
630   if(xmlXPathNodeSetGetLength(pobj->nodesetval) > 1) {
631     err = "ambiguous section";
632     goto bad;
633   }
634
635   node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
636   if(!strcmp((char *)node->name, "check")) {
637     err = "can't cd into a check, use 'check' instead";
638     goto bad;
639   }
640   path = (char *)xmlGetNodePath(node);
641   if(strncmp(path, "/noit/", strlen("/noit/")) && strcmp(path, "/noit")) {
642     err = "new path outside out tree";
643     goto bad;
644   }
645   free(info->path);
646   if(!strcmp(path, "/noit"))
647     info->path = strdup("/");
648   else
649     info->path = strdup((char *)xmlGetNodePath(node) + strlen("/noit"));
650   if(pobj) xmlXPathFreeObject(pobj);
651   if(closure) noit_console_state_pop(ncct, argc, argv, NULL, NULL);
652   return 0;
653  bad:
654   if(pobj) xmlXPathFreeObject(pobj);
655   nc_printf(ncct, "%s [%s]\n", err, xpath);
656   return -1;
657 }
658 static int
659 noit_console_config_show(noit_console_closure_t ncct,
660                          int argc, char **argv,
661                          noit_console_state_t *state, void *closure) {
662   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
663   const char *k;
664   int klen;
665   void *data;
666   int i, cnt, titled = 0, cliplen = 0;
667   const char *path = "", *basepath = NULL;
668   char xpath[1024];
669   noit_conf_t_userdata_t *info = NULL;
670   noit_hash_table *config;
671   xmlXPathObjectPtr pobj = NULL;
672   xmlXPathContextPtr xpath_ctxt = NULL, current_ctxt;
673   xmlDocPtr master_config = NULL;
674   xmlNodePtr node;
675
676   noit_conf_xml_xpath(&master_config, &xpath_ctxt);
677   if(argc > 1) {
678     nc_printf(ncct, "too many arguments\n");
679     return -1;
680   }
681
682   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
683   if(info) path = basepath = info->path;
684   if(!info && argc == 0) {
685     nc_printf(ncct, "argument required when not in configuration mode\n");
686     return -1;
687   }
688
689   if(argc == 1) path = argv[0];
690   if(!basepath) basepath = path;
691
692   /* { / } is a special case */
693   if(!strcmp(basepath, "/")) basepath = "";
694   if(!strcmp(path, "/")) path = "";
695
696   if(!master_config) {
697     nc_printf(ncct, "no config\n");
698     return -1;
699   }
700
701   /* { / } is the only path that will end with a /
702    * in XPath { / / * } means something _entirely different than { / * }
703    * Ever notice how it is hard to describe xpath in C comments?
704    */
705   /* We don't want to show the root node */
706   cliplen = strlen("/noit/");
707
708   /* If we are in configuration mode
709    * and we are without an argument or the argument is absolute,
710    * clip the current path off */
711   if(info && (argc == 0 || path[0] != '/')) cliplen += strlen(basepath);
712   if(!path[0] || path[0] == '/') /* base only, or absolute path requested */
713     snprintf(xpath, sizeof(xpath), "/noit%s/@*", path);
714   else
715     snprintf(xpath, sizeof(xpath), "/noit%s/%s/@*", basepath, path);
716
717   current_ctxt = xpath_ctxt;
718   pobj = xmlXPathEval((xmlChar *)xpath, current_ctxt);
719   if(!pobj || pobj->type != XPATH_NODESET) {
720     nc_printf(ncct, "no such object\n");
721     goto bad;
722   }
723   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
724   titled = 0;
725   for(i=0; i<cnt; i++) {
726     node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
727     if(!strcmp((char *)node->name, "check")) continue;
728     if(node->children && node->children == xmlGetLastChild(node) &&
729       xmlNodeIsText(node->children)) {
730       if(!titled++) nc_printf(ncct, "== Section Settings ==\n");
731       nc_printf(ncct, "%s: %s\n", xmlGetNodePath(node) + cliplen,
732                 xmlXPathCastNodeToString(node->children));
733     }
734   }
735   xmlXPathFreeObject(pobj);
736
737   /* Print out all the config settings */
738   titled = 0;
739   config = noit_conf_get_hash(node, "config");
740   while(noit_hash_next(config, &iter, &k, &klen, &data)) {
741     if(!titled++) nc_printf(ncct, "== Section [Aggregated] Config ==\n");
742     nc_printf(ncct, "config::%s: %s\n", k, (const char *)data);
743   }
744   noit_hash_destroy(config, free, free);
745   free(config);
746
747   /* _shorten string_ turning last { / @ * } to { / * } */
748   strlcpy(xpath + strlen(xpath) - 2, "*", 2);
749   pobj = xmlXPathEval((xmlChar *)xpath, current_ctxt);
750   if(!pobj || pobj->type != XPATH_NODESET) {
751     nc_printf(ncct, "no such object\n");
752     goto bad;
753   }
754   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
755   titled = 0;
756   for(i=0; i<cnt; i++) {
757     node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
758     if(!strcmp((char *)node->name, "check")) continue;
759     if(!strcmp((char *)xmlGetNodePath(node) + cliplen, "config")) continue;
760     if(!(node->children && node->children == xmlGetLastChild(node) &&
761          xmlNodeIsText(node->children))) {
762       if(!titled++) nc_printf(ncct, "== Subsections ==\n");
763       nc_printf(ncct, "%s\n", xmlGetNodePath(node) + cliplen);
764     }
765   }
766
767   titled = 0;
768   for(i=0; i<cnt; i++) {
769     node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
770     if(!strcmp((char *)node->name, "check")) {
771       int busted = 1;
772       xmlAttr *attr;
773       char *uuid_str = "undefined";
774
775       if(!titled++) nc_printf(ncct, "== Checks ==\n");
776
777       for(attr=node->properties; attr; attr = attr->next) {
778         if(!strcmp((char *)attr->name, "uuid"))
779           uuid_str = (char *)xmlXPathCastNodeToString(attr->children);
780       }
781       if(uuid_str) {
782         uuid_t checkid;
783         nc_printf(ncct, "check[@uuid=\"%s\"] ", uuid_str);
784         if(uuid_parse(uuid_str, checkid) == 0) {
785           noit_check_t *check;
786           check = noit_poller_lookup(checkid);
787           if(check) {
788             busted = 0;
789             nc_printf(ncct, "%s`%s`%s", check->target, check->module, check->name);
790           }
791         }
792       }
793       else
794         nc_printf(ncct, "%s ", xmlGetNodePath(node) + cliplen);
795       if(busted) nc_printf(ncct, "[check not in running system]");
796       nc_write(ncct, "\n", 1);
797     }
798   }
799   xmlXPathFreeObject(pobj);
800   return 0;
801  bad:
802   if(pobj) xmlXPathFreeObject(pobj);
803   return -1;
804 }
805
806 static char *
807 conf_t_check_prompt(EditLine *el) {
808   noit_console_closure_t ncct;
809   noit_conf_t_userdata_t *info;
810   noit_check_t *check;
811   static char *tl = "noit(conf)# ";
812   static char *pfmt = "noit(conf:%s%s%s)# ";
813
814   el_get(el, EL_USERDATA, (void *)&ncct);
815   if(!ncct) return tl;
816   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
817   if(!info) return tl;
818
819   check = noit_poller_lookup(info->current_check);
820   if(check &&
821      check->target && check->target[0] &&
822      check->name && check->name[0])
823     snprintf(info->prompt, sizeof(info->prompt),
824              pfmt, check->target, "`", check->name);
825   else {
826     char uuid_str[37];
827     uuid_unparse_lower(info->current_check, uuid_str);
828     snprintf(info->prompt, sizeof(info->prompt), pfmt, "[", uuid_str, "]");
829   }
830   return info->prompt;
831 }
832 static char *
833 conf_t_prompt(EditLine *el) {
834   noit_console_closure_t ncct;
835   noit_conf_t_userdata_t *info;
836   static char *tl = "noit(conf)# ";
837   static char *pfmt = "noit(conf:%s%s)# ";
838   int path_len, max_len;
839
840   el_get(el, EL_USERDATA, (void *)&ncct);
841   if(!ncct) return tl;
842   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
843   if(!info) return tl;
844
845   path_len = strlen(info->path);
846   max_len = sizeof(info->prompt) - (strlen(pfmt) - 4 /* %s%s */) - 1 /* \0 */;
847   if(path_len > max_len)
848     snprintf(info->prompt, sizeof(info->prompt),
849              pfmt, "...", info->path + path_len - max_len + 3 /* ... */);
850   else
851     snprintf(info->prompt, sizeof(info->prompt), pfmt, "", info->path);
852   return info->prompt;
853 }
854 static int
855 noit_conf_checks_reload(noit_console_closure_t ncct,
856                         int argc, char **argv,
857                         noit_console_state_t *state, void *closure) {
858   if(noit_conf_reload(ncct, argc, argv, state, closure)) return -1;
859   noit_poller_reload(NULL);
860   return 0;
861 }
862
863 static int
864 validate_attr_set_scope(noit_conf_t_userdata_t *info,
865                         struct _valid_attr_t *attrinfo) {
866   int len;
867   len = strlen(attrinfo->scope);
868   if(strncmp(info->path, attrinfo->scope, len) ||
869      (info->path[len] != '\0' && info->path[len] != '/')) {
870     return -1;
871   }
872   return 0;
873 }
874 static int
875 replace_config(noit_console_closure_t ncct,
876                noit_conf_t_userdata_t *info, const char *name,
877                const char *value) {
878   int i, cnt, rv = -1, active = 0;
879   xmlXPathObjectPtr pobj = NULL;
880   xmlXPathContextPtr xpath_ctxt = NULL;
881   xmlNodePtr node, confignode;
882   char xpath[1024], *path;
883
884   path = info->path;
885   if(!strcmp(path, "/")) path = "";
886
887   noit_conf_xml_xpath(NULL, &xpath_ctxt);
888
889   /* Only if checks will fixate this attribute shall we check for
890    * child <check> nodes.
891    * NOTE: this return nothing and "seems" okay if we are _in_
892    *       a <check> node.  That case is handled below.
893    */
894   snprintf(xpath, sizeof(xpath), "/noit/%s//check[@uuid]", path);
895   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
896   if(!pobj || pobj->type != XPATH_NODESET) goto out;
897   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
898   for(i=0; i<cnt; i++) {
899     uuid_t checkid;
900     node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
901     if(noit_conf_get_uuid(node, "@uuid", checkid)) {
902       noit_check_t *check;
903       check = noit_poller_lookup(checkid);
904       if(NOIT_CHECK_LIVE(check)) active++;
905     }
906   }
907   if(pobj) xmlXPathFreeObject(pobj);
908
909   snprintf(xpath, sizeof(xpath), "/noit/%s", path);
910   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
911   if(!pobj || pobj->type != XPATH_NODESET) goto out;
912   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
913   if(cnt != 1) {
914     nc_printf(ncct, "Internal error: context node disappeared\n");
915     goto out;
916   }
917   node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
918   if(strcmp((const char *)node->name, "check")) {
919     uuid_t checkid;
920     /* Detect if  we are actually a <check> node and attempting to
921      * change something we shouldn't.
922      * This is the counterpart noted above.
923      */
924     if(noit_conf_get_uuid(node, "@uuid", checkid)) {
925       noit_check_t *check;
926       check = noit_poller_lookup(checkid);
927       if(NOIT_CHECK_LIVE(check)) active++;
928     }
929   }
930 #ifdef UNSAFE_RECONFIG
931   if(active) {
932     nc_printf(ncct, "Cannot set '%s', it would effect %d live check(s)\n",
933               name, active);
934     goto out;
935   }
936 #endif
937   if(pobj) xmlXPathFreeObject(pobj);
938
939   /* Here we want to remove /noit/path/config/name */
940   snprintf(xpath, sizeof(xpath), "/noit/%s/config/%s", path, name);
941   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
942   if(!pobj || pobj->type != XPATH_NODESET) goto out;
943   if(xmlXPathNodeSetGetLength(pobj->nodesetval) > 0) {
944     xmlNodePtr toremove;
945     toremove = xmlXPathNodeSetItem(pobj->nodesetval, 0);
946     xmlUnlinkNode(toremove);
947   }
948   /* TODO: if there are no more children of config, remove config? */
949   if(value) {
950     if(pobj) xmlXPathFreeObject(pobj);
951     /* He we create config if needed and place a child node under it */
952     snprintf(xpath, sizeof(xpath), "/noit/%s/config", path);
953     pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
954     if(!pobj || pobj->type != XPATH_NODESET) goto out;
955     if(xmlXPathNodeSetGetLength(pobj->nodesetval) == 0) {
956       if(pobj) xmlXPathFreeObject(pobj);
957       snprintf(xpath, sizeof(xpath), "/noit/%s", path);
958       pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
959       if(!pobj || pobj->type != XPATH_NODESET) goto out;
960       if(xmlXPathNodeSetGetLength(pobj->nodesetval) != 1) {
961         nc_printf(ncct, "Node disappeared from under you!\n");
962         goto out;
963       }
964       confignode = xmlNewChild(xmlXPathNodeSetItem(pobj->nodesetval, 0),
965                                NULL, (xmlChar *)"config", NULL);
966       if(confignode == NULL) {
967         nc_printf(ncct, "Error creating config child node.\n");
968         goto out;
969       }
970     }
971     else confignode = xmlXPathNodeSetItem(pobj->nodesetval, 0);
972
973     assert(confignode);
974     /* Now we create a child */
975     xmlNewChild(confignode, NULL, (xmlChar *)name, (xmlChar *)value);
976    
977   }
978   rv = 0;
979  out:
980   if(pobj) xmlXPathFreeObject(pobj);
981   return rv;
982 }
983 static int
984 replace_attr(noit_console_closure_t ncct,
985              noit_conf_t_userdata_t *info, struct _valid_attr_t *attrinfo,
986              const char *value) {
987   int i, cnt, rv = -1, active = 0;
988   xmlXPathObjectPtr pobj = NULL;
989   xmlXPathContextPtr xpath_ctxt = NULL;
990   xmlNodePtr node;
991   char xpath[1024], *path;
992
993   path = info->path;
994   if(!strcmp(path, "/")) path = "";
995
996   noit_conf_xml_xpath(NULL, &xpath_ctxt);
997   if(attrinfo->checks_fixate) {
998     /* Only if checks will fixate this attribute shall we check for
999      * child <check> nodes.
1000      * NOTE: this return nothing and "seems" okay if we are _in_
1001      *       a <check> node.  That case is handled below.
1002      */
1003     snprintf(xpath, sizeof(xpath), "/noit/%s//check[@uuid]", path);
1004     pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
1005     if(!pobj || pobj->type != XPATH_NODESET) goto out;
1006     cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
1007     for(i=0; i<cnt; i++) {
1008       uuid_t checkid;
1009       node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
1010       if(noit_conf_get_uuid(node, "@uuid", checkid)) {
1011         noit_check_t *check;
1012         check = noit_poller_lookup(checkid);
1013         if(NOIT_CHECK_LIVE(check)) active++;
1014       }
1015     }
1016     if(pobj) xmlXPathFreeObject(pobj);
1017   }
1018   snprintf(xpath, sizeof(xpath), "/noit/%s", path);
1019   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
1020   if(!pobj || pobj->type != XPATH_NODESET) goto out;
1021   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
1022   if(cnt != 1) {
1023     nc_printf(ncct, "Internal error: context node disappeared\n");
1024     goto out;
1025   }
1026   node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
1027   if(attrinfo->checks_fixate &&
1028      !strcmp((const char *)node->name, "check")) {
1029     uuid_t checkid;
1030     /* Detect if  we are actually a <check> node and attempting to
1031      * change something we shouldn't.
1032      * This is the counterpart noted above.
1033      */
1034     if(noit_conf_get_uuid(node, "@uuid", checkid)) {
1035       noit_check_t *check;
1036       check = noit_poller_lookup(checkid);
1037       if(NOIT_CHECK_LIVE(check)) active++;
1038     }
1039   }
1040   if(active) {
1041     nc_printf(ncct, "Cannot set '%s', it would effect %d live check(s)\n",
1042               attrinfo->name, active);
1043     goto out;
1044   }
1045   xmlUnsetProp(node, (xmlChar *)attrinfo->name);
1046   if(value)
1047     xmlSetProp(node, (xmlChar *)attrinfo->name, (xmlChar *)value);
1048   rv = 0;
1049  out:
1050   if(pobj) xmlXPathFreeObject(pobj);
1051   return rv;
1052 }
1053 int
1054 noit_conf_check_set_attr(noit_console_closure_t ncct,
1055                          int argc, char **argv,
1056                          noit_console_state_t *state, void *closure) {
1057   struct _valid_attr_t *attrinfo = closure;
1058   noit_conf_t_userdata_t *info;
1059
1060   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
1061   if(!info || validate_attr_set_scope(info, attrinfo)) {
1062     nc_printf(ncct, "'%s' attribute only valid in %s scope\n",
1063               attrinfo->name, attrinfo->scope);
1064     return -1;
1065   }
1066
1067   if(argc != 1) {
1068     nc_printf(ncct, "set requires exactly one value\n");
1069     return -1;
1070   }
1071   /* Okay, we have an attribute and it should be set/replaced on the
1072    * current path.
1073    */
1074   if(replace_attr(ncct, info, attrinfo, argv[0])) {
1075     return -1;
1076   }
1077
1078   /* So, we updated an attribute, so we need to reload all checks
1079    * that are descendent-or-self of this node.
1080    */
1081   if(!strncmp(info->path, "/checks", strlen("/checks")))
1082     refresh_subchecks(ncct, info);
1083   if(!strncmp(info->path, "/filtersets", strlen("/filtersets")))
1084     noit_refresh_filtersets(ncct, info);
1085   return 0;
1086 }
1087
1088 int
1089 noit_conf_check_unset_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 != 0) {
1103     nc_printf(ncct, "no arguments allowed to this command.\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, NULL)) {
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, "/filterset", strlen("/filterest")))
1119     noit_refresh_filtersets(ncct, info);
1120   return 0;
1121 }
1122
1123 int
1124 noit_console_config_setconfig(noit_console_closure_t ncct,
1125                                 int argc, char **argv,
1126                                 noit_console_state_t *state, void *closure) {
1127   noit_conf_t_userdata_t *info;
1128
1129   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
1130
1131   if(argc != 2) {
1132     nc_printf(ncct, "two arguments required.\n");
1133     return -1;
1134   }
1135   /* Okay, we have an child name and it should be culled from
1136    * current path/config.
1137    */
1138   if(replace_config(ncct, info, argv[0], argv[1])) {
1139     return -1;
1140   }
1141
1142   /* So, we updated an attribute, so we need to reload all checks
1143    * that are descendent-or-self of this node.
1144    */
1145   refresh_subchecks(ncct, info);
1146   return 0;
1147 }
1148
1149 int
1150 noit_console_config_unsetconfig(noit_console_closure_t ncct,
1151                                 int argc, char **argv,
1152                                 noit_console_state_t *state, void *closure) {
1153   noit_conf_t_userdata_t *info;
1154
1155   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
1156
1157   if(argc != 1) {
1158     nc_printf(ncct, "one argument required.\n");
1159     return -1;
1160   }
1161   /* Okay, we have an child name and it should be culled from
1162    * current path/config.
1163    */
1164   if(replace_config(ncct, info, argv[0], NULL)) {
1165     return -1;
1166   }
1167
1168   /* So, we updated an attribute, so we need to reload all checks
1169    * that are descendent-or-self of this node.
1170    */
1171   refresh_subchecks(ncct, info);
1172   return 0;
1173 }
1174
1175
1176 #define NEW_STATE(a) (a) = noit_console_state_alloc()
1177 #define ADD_CMD(a,cmd,func,ss,c) \
1178   noit_console_state_add_cmd((a), \
1179     NCSCMD(cmd, func, ss, c))
1180 #define DELEGATE_CMD(a,cmd,ss) \
1181   noit_console_state_add_cmd((a), \
1182     NCSCMD(cmd, noit_console_state_delegate, ss, NULL))
1183
1184 static
1185 void register_console_config_commands() {
1186   cmd_info_t *showcmd;
1187   noit_console_state_t *tl, *_conf_state, *_conf_t_state,
1188                        *_conf_t_check_state,
1189                        *_write_state, *_attr_state,
1190                        *_unset_state, *_uattr_state;
1191
1192   tl = noit_console_state_initial();
1193
1194   /* write <terimal|memory|file> */
1195   NEW_STATE(_write_state);
1196   ADD_CMD(_write_state, "terminal", noit_conf_write_terminal, NULL, NULL);
1197   ADD_CMD(_write_state, "file", noit_conf_write_file, NULL, NULL);
1198   /* write memory?  It's to a file, but I like router syntax */
1199   ADD_CMD(_write_state, "memory", noit_conf_write_file, NULL, NULL);
1200
1201   /* attribute <attrname> <value> */
1202   NEW_STATE(_attr_state);
1203   noit_console_state_add_check_attrs(_attr_state, noit_conf_check_set_attr);
1204  
1205   /* no attribute <attrname> <value> */
1206   NEW_STATE(_uattr_state);
1207   noit_console_state_add_check_attrs(_uattr_state, noit_conf_check_unset_attr);
1208
1209   NEW_STATE(_unset_state);
1210   DELEGATE_CMD(_unset_state, "attribute", _uattr_state);
1211   ADD_CMD(_unset_state, "section", noit_console_config_section, NULL, (void *)1);
1212   ADD_CMD(_unset_state, "config", noit_console_config_unsetconfig, NULL, NULL);
1213   ADD_CMD(_unset_state, "check", noit_console_config_nocheck, NULL, NULL);
1214  
1215   NEW_STATE(_conf_t_check_state);
1216   _conf_t_check_state->console_prompt_function = conf_t_check_prompt;
1217   DELEGATE_CMD(_conf_t_check_state, "attribute", _attr_state);
1218   DELEGATE_CMD(_conf_t_check_state, "no", _unset_state);
1219   ADD_CMD(_conf_t_check_state, "config", noit_console_config_setconfig, NULL, NULL);
1220   ADD_CMD(_conf_t_check_state, "status", noit_console_show_check, NULL, NULL);
1221   ADD_CMD(_conf_t_check_state, "exit", noit_console_config_cd, NULL, "..");
1222   ADD_CMD(_conf_t_check_state, "check", noit_console_check, _conf_t_check_state, "..");
1223
1224   NEW_STATE(_conf_t_state);
1225   _conf_t_state->console_prompt_function = conf_t_prompt;
1226   noit_console_state_add_cmd(_conf_t_state, &console_command_exit);
1227   ADD_CMD(_conf_t_state, "ls", noit_console_config_show, NULL, NULL);
1228   ADD_CMD(_conf_t_state, "cd", noit_console_config_cd, NULL, NULL);
1229   ADD_CMD(_conf_t_state, "config", noit_console_config_setconfig, NULL, NULL);
1230   ADD_CMD(_conf_t_state, "section", noit_console_config_section, NULL, (void *)0);
1231   ADD_CMD(_conf_t_state, "check", noit_console_check, _conf_t_check_state, NULL);
1232
1233   showcmd = noit_console_state_get_cmd(tl, "show");
1234   ADD_CMD(showcmd->dstate, "check", noit_console_show_check, NULL, NULL);
1235
1236   DELEGATE_CMD(_conf_t_state, "write", _write_state);
1237   DELEGATE_CMD(_conf_t_state, "attribute", _attr_state);
1238   DELEGATE_CMD(_conf_t_state, "no", _unset_state);
1239
1240   NEW_STATE(_conf_state);
1241   ADD_CMD(_conf_state, "terminal", noit_console_state_conf_terminal, _conf_t_state, NULL);
1242
1243   ADD_CMD(tl, "configure", noit_console_state_delegate, _conf_state, NULL);
1244   ADD_CMD(tl, "write", noit_console_state_delegate, _write_state, NULL);
1245   ADD_CMD(tl, "reload", noit_conf_checks_reload, NULL, NULL);
1246 }
1247
Note: See TracBrowser for help on using the browser.