root/src/noit_conf_checks.c

Revision 103da28cd6e7b1d553189effd0c2ac9b132da7a5, 42.1 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 10 months ago)

Make ongoing stats collection for a check uniform across all checks.
Almost every check carries a stats_t in it's check closure that it
populates and in it's final phase sets that stats structure use
noit_check_set_stats. Instead of every check doing this its own
unique way, we introduct and "inprogress" stats_t structure into
the noit_check_t.stats structure so that all checks can do it the
same way.

This change also improves the console and xml outputs showing checks
to include the "inprogress" metric data.

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