root/src/noit_conf_checks.c

Revision 31eed12e59a6d35dbdc8812d8a54d76864f05df4, 45.0 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 5 years ago)

if a bad uuid gets in there, this crashes

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