root/src/noit_conf_checks.c

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

various changes to avoid dereferencing type-punned pointers and breaking strict-aliasing rules, refs #34

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