root/src/noit_conf.c

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

rework a lot of config stuff, add docs in a bad place.. README.txt? come on Theo.

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