root/src/noit_conf_checks.c

Revision 9c44e5a54c4ada69e5e61476fc5775b6e4d96c14, 44.1 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 3 months ago)

Make stats on checks use epoch memory reclamation.

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