root/src/noit_conf_checks.c

Revision 56c7e9dd00981d9018a28e7adf1fb6144821c51e, 44.6 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 5 years ago)

refs #37... lots of progress here

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