root/src/noit_conf_checks.c

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

fixes #126

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