root/src/noit_conf.c

Revision 4dccf830640c308658d62f7896f78488734381bc, 43.7 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 6 years ago)

xpointer-esque inheritence... actually quite neat

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