root/src/noit_conf.c

Revision 322ceb0139cf145a94006446310d0aa106998056, 41.4 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 4 years ago)

This allows for a

<include file="fileinsamedirasmaster.conf"/>

tp be placed anywhere in the master config of either noitd or stratcond.
The include is processed by parsing the specified file and putting all
children of the root element of the included document as replacements for
the children of the <include> node itself.

Special care is taken to make it look like we did not do this when we go to
write the config anywhere.

refs #254

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2007, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  *       notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  *       copyright notice, this list of conditions and the following
13  *       disclaimer in the documentation and/or other materials provided
14  *       with the distribution.
15  *     * Neither the name OmniTI Computer Consulting, Inc. nor the names
16  *       of its contributors may be used to endorse or promote products
17  *       derived from this software without specific prior written
18  *       permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include "noit_defines.h"
34
35 #include <stdio.h>
36 #include <unistd.h>
37 #include <fcntl.h>
38 #include <errno.h>
39 #include <assert.h>
40 #include <libxml/parser.h>
41 #include <libxml/tree.h>
42 #include <libxml/xpath.h>
43 #include <zlib.h>
44
45 #include "noit_conf.h"
46 #include "noit_check.h"
47 #include "noit_console.h"
48 #include "utils/noit_hash.h"
49 #include "utils/noit_log.h"
50 #include "utils/noit_b64.h"
51
52 /* tmp hash impl, replace this with something nice */
53 static noit_hash_table _tmp_config = NOIT_HASH_EMPTY;
54 static xmlDocPtr master_config = NULL;
55 static int config_include_cnt = -1;
56 static struct {
57   xmlNodePtr insertion_point;
58   xmlNodePtr old_children;
59   xmlDocPtr doc;
60   xmlNodePtr root;
61 } *config_include_nodes = NULL;
62
63 static char *root_node_name = NULL;
64 static char master_config_file[PATH_MAX] = "";
65 static xmlXPathContextPtr xpath_ctxt = NULL;
66
67 /* This is used to notice config changes and journal the config out
68  * using a user-specified function.  It supports allowing multiple config
69  * changed to coalesce so you don't write out 1000 changes in a few seconds.
70  */
71 static u_int32_t __config_gen = 0;
72 static u_int32_t __config_coalesce = 0;
73 static u_int32_t __config_coalesce_time = 0;
74 void noit_conf_coalesce_changes(u_int32_t seconds) {
75   __config_coalesce_time = seconds;
76 }
77 void noit_conf_mark_changed() {
78   /* increment the change counter -- in case anyone cares */
79   __config_gen++;
80   /* reset the coalesce counter.  It is decremented each second and
81    * the journal function fires on a transition from 1 => 0
82    */
83   __config_coalesce = __config_coalesce_time;
84 }
85 struct recurrent_journaler {
86   int (*journal_config)(void *);
87   void *jc_closure;
88 };
89 static int
90 noit_conf_watch_config_and_journal(eventer_t e, int mask, void *closure,
91                                    struct timeval *now) {
92   struct recurrent_journaler *rj = closure;
93   eventer_t newe;
94
95   if(__config_coalesce == 1)
96     rj->journal_config(rj->jc_closure);
97   if(__config_coalesce > 0)
98     __config_coalesce--;
99
100   /* Schedule the same event to fire a second form now */
101   newe = eventer_alloc();
102   gettimeofday(&newe->whence, NULL);
103   newe->whence.tv_sec += 1;
104   newe->mask = EVENTER_TIMER;
105   newe->callback = noit_conf_watch_config_and_journal;
106   newe->closure = closure;
107   eventer_add(newe);
108   return 0;
109 }
110 void
111 noit_conf_watch_and_journal_watchdog(int (*f)(void *), void *c) {
112   static int callbacknamed = 0;
113   struct recurrent_journaler *rj;
114   struct timeval __now;
115
116   if(!callbacknamed) {
117     callbacknamed = 1;
118     eventer_name_callback("noit_conf_watch_config_and_journal",
119                           noit_conf_watch_config_and_journal);
120   }
121   rj = calloc(1, sizeof(*rj));
122   rj->journal_config = f;
123   rj->jc_closure = c;
124   gettimeofday(&__now, NULL);
125   noit_conf_watch_config_and_journal(NULL, EVENTER_TIMER, rj, &__now);
126 }
127
128 static noit_hash_table _compiled_fallback = NOIT_HASH_EMPTY;
129 static struct {
130   const char *key;
131   const char *val;
132 } config_info[] = {
133   /*
134    * These are compile-time fallbacks to be used in the event
135    * that the current running config does not have values for
136    * these config paths.
137    *
138    * PLEASE: keep them alphabetically sorted.
139    */
140   { "/%s/eventer/@implementation", DEFAULT_EVENTER },
141   { "/%s/modules/@directory", MODULES_DIR },
142
143   { NULL, NULL }
144 };
145
146 void noit_conf_xml_error_func(void *ctx, const char *format, ...) {
147   struct timeval __now;
148   noit_log_stream_t ls = ctx;
149   va_list arg;
150   if(!ls) return;
151   va_start(arg, format);
152   gettimeofday(&__now,  NULL);
153   noit_vlog(ls, &__now, __FILE__, __LINE__, format, arg);
154   va_end(arg);
155 }
156 void noit_conf_xml_error_ext_func(void *ctx, xmlErrorPtr err) {
157   struct timeval __now;
158   noit_log_stream_t ls = ctx;
159   if(!ls) return;
160   gettimeofday(&__now,  NULL);
161   if(err->file)
162     noit_log(ls, &__now, err->file, err->line,
163              "XML error [%d/%d] in %s on line %d %s\n",
164              err->domain, err->code, err->file, err->line, err->message);
165   else
166     noit_log(ls, &__now, err->file, err->line,
167              "XML error [%d/%d] %s\n",
168              err->domain, err->code, err->message);
169 }
170
171
172 DECLARE_CHECKER(name)
173 void noit_conf_init(const char *toplevel) {
174   int i;
175   char keystr[256];
176   COMPILE_CHECKER(name, "^[-_\\.:/a-zA-Z0-9]+$");
177   xmlSetGenericErrorFunc(noit_error, noit_conf_xml_error_func);
178   xmlSetStructuredErrorFunc(noit_error, noit_conf_xml_error_ext_func);
179   for(i = 0; config_info[i].key != NULL; i++) {
180     snprintf(keystr, sizeof(keystr), config_info[i].key, toplevel);
181     noit_hash_store(&_compiled_fallback,
182                     strdup(keystr), strlen(keystr),
183                     (void *)strdup(config_info[i].val));
184   }
185   xmlKeepBlanksDefault(0);
186   xmlInitParser();
187   xmlXPathInit();
188 }
189
190 void
191 noit_conf_magic_separate(xmlDocPtr doc) {
192   assert(config_include_cnt != -1);
193   if(config_include_nodes) {
194     int i;
195     for(i=0; i<config_include_cnt; i++) {
196       if(config_include_nodes[i].doc) {
197         xmlNodePtr n;
198         for(n=config_include_nodes[i].insertion_point->children;
199             n; n = n->next)
200           n->parent = config_include_nodes[i].root;
201         config_include_nodes[i].insertion_point->children =
202           config_include_nodes[i].old_children;
203         xmlFreeDoc(config_include_nodes[i].doc);
204       }
205     }
206     free(config_include_nodes);
207   }
208   config_include_nodes = NULL;
209   config_include_cnt = -1;
210 }
211 void
212 noit_conf_kansas_city_shuffle_redo(xmlDocPtr doc) {
213   if(config_include_nodes) {
214     int i;
215     for(i=0; i<config_include_cnt; i++) {
216       if(config_include_nodes[i].doc) {
217         xmlNodePtr n;
218         config_include_nodes[i].insertion_point->children =
219           config_include_nodes[i].root->children;
220         for(n=config_include_nodes[i].insertion_point->children;
221             n; n = n->next)
222           n->parent = config_include_nodes[i].insertion_point;
223       }
224     }
225   }
226 }
227 void
228 noit_conf_kansas_city_shuffle_undo(xmlDocPtr doc) {
229   if(config_include_nodes) {
230     int i;
231     for(i=0; i<config_include_cnt; i++) {
232       if(config_include_nodes[i].doc) {
233         xmlNodePtr n;
234         for(n=config_include_nodes[i].insertion_point->children;
235             n; n = n->next)
236           n->parent = config_include_nodes[i].root;
237         config_include_nodes[i].insertion_point->children =
238           config_include_nodes[i].old_children;
239       }
240     }
241   }
242 }
243 int
244 noit_conf_magic_mix(const char *parentfile, xmlDocPtr doc) {
245   xmlXPathContextPtr mix_ctxt = NULL;
246   xmlXPathObjectPtr pobj = NULL;
247   xmlNodePtr node;
248   int i, cnt, rv = 0;
249
250   assert(config_include_cnt == -1);
251
252   config_include_cnt = 0;
253   mix_ctxt = xmlXPathNewContext(doc);
254   pobj = xmlXPathEval((xmlChar *)"//include[@file]", mix_ctxt);
255   if(!pobj) goto out;
256   if(pobj->type != XPATH_NODESET) goto out;
257   if(xmlXPathNodeSetIsEmpty(pobj->nodesetval)) goto out;
258   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
259   if(cnt > 0)
260     config_include_nodes = calloc(cnt, sizeof(*config_include_nodes));
261   for(i=0; i<cnt; i++) {
262     char *path, *infile;
263     node = xmlXPathNodeSetItem(pobj->nodesetval, i);
264     path = (char *)xmlGetProp(node, (xmlChar *)"file");
265     if(!path) continue;
266     if(*path == '/') infile = strdup(path);
267     else {
268       char *cp;
269       infile = malloc(PATH_MAX);
270       strlcpy(infile, parentfile, PATH_MAX);
271       for(cp = infile + strlen(infile) - 1; cp > infile; cp--) {
272         if(*cp == '/') { *cp = '\0'; break; }
273         else *cp = '\0';
274       }
275       strlcat(infile, "/", PATH_MAX);
276       strlcat(infile, path, PATH_MAX);
277     }
278     config_include_nodes[i].doc = xmlParseFile(infile);
279     if(config_include_nodes[i].doc) {
280       xmlNodePtr n;
281       config_include_nodes[i].insertion_point = node;
282       config_include_nodes[i].root = xmlDocGetRootElement(config_include_nodes[i].doc);
283       config_include_nodes[i].old_children = node->children;
284       node->children = config_include_nodes[i].root->children;
285       for(n=node->children; n; n = n->next)
286         n->parent = config_include_nodes[i].insertion_point;
287     }
288     else {
289       noitL(noit_error, "Could not load: '%s'\n", infile);
290       rv = -1;
291     }
292     free(infile);
293   }
294   config_include_cnt = cnt;
295   noitL(noit_debug, "Processed %d includes\n", config_include_cnt);
296  out:
297   if(pobj) xmlXPathFreeObject(pobj);
298   if(mix_ctxt) xmlXPathFreeContext(mix_ctxt);
299   return rv;
300 }
301
302 int noit_conf_load(const char *path) {
303   int rv = 0;
304   xmlDocPtr new_config;
305   xmlNodePtr root;
306   new_config = xmlParseFile(path);
307   if(new_config) {
308     root = xmlDocGetRootElement(new_config);
309     if(root_node_name) free(root_node_name);
310     root_node_name = strdup((char *)root->name);
311
312     if(master_config) {
313       /* separate all includes */
314       noit_conf_magic_separate(master_config);
315       xmlFreeDoc(master_config);
316     }
317     if(xpath_ctxt) xmlXPathFreeContext(xpath_ctxt);
318
319     master_config = new_config;
320     /* mixin all the includes */
321     if(noit_conf_magic_mix(path, master_config)) rv = -1;
322
323     xpath_ctxt = xmlXPathNewContext(master_config);
324     if(path != master_config_file)
325       if(realpath(path, master_config_file) == NULL)
326         noitL(noit_error, "realpath failed: %s\n", strerror(errno));
327     noit_conf_mark_changed();
328     return rv;
329   }
330   rv = -1;
331   return rv;
332 }
333
334 char *noit_conf_config_filename() {
335   return strdup(master_config_file);
336 }
337
338 int noit_conf_xml_xpath(xmlDocPtr *mc, xmlXPathContextPtr *xp) {
339   if(mc) *mc = master_config;
340   if(xp) *xp = xpath_ctxt;
341   return 0;
342 }
343 int noit_conf_save(const char *path) {
344   return -1;
345 }
346
347 void noit_conf_get_elements_into_hash(noit_conf_section_t section,
348                                       const char *path,
349                                       noit_hash_table *table) {
350   int i, cnt;
351   xmlXPathObjectPtr pobj = NULL;
352   xmlXPathContextPtr current_ctxt;
353   xmlNodePtr current_node = (xmlNodePtr)section;
354   xmlNodePtr node;
355
356   current_ctxt = xpath_ctxt;
357   if(current_node) {
358     current_ctxt = xmlXPathNewContext(master_config);
359     current_ctxt->node = current_node;
360   }
361   pobj = xmlXPathEval((xmlChar *)path, current_ctxt);
362   if(!pobj) goto out;
363   if(pobj->type != XPATH_NODESET) goto out;
364   if(xmlXPathNodeSetIsEmpty(pobj->nodesetval)) goto out;
365   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
366   for(i=0; i<cnt; i++) {
367     char *value;
368     node = xmlXPathNodeSetItem(pobj->nodesetval, i);
369     value = (char *)xmlXPathCastNodeToString(node);
370     noit_hash_replace(table,
371                       strdup((char *)node->name), strlen((char *)node->name),
372                       strdup(value), free, free);
373     xmlFree(value);
374   }
375  out:
376   if(pobj) xmlXPathFreeObject(pobj);
377   if(current_ctxt && current_ctxt != xpath_ctxt)
378     xmlXPathFreeContext(current_ctxt);
379 }
380 void noit_conf_get_into_hash(noit_conf_section_t section,
381                              const char *path,
382                              noit_hash_table *table) {
383   int cnt;
384   xmlXPathObjectPtr pobj = NULL;
385   xmlXPathContextPtr current_ctxt;
386   xmlNodePtr current_node = (xmlNodePtr)section;
387   xmlNodePtr node, parent_node;
388   char xpath_expr[1024];
389   char *inheritid;
390
391   current_ctxt = xpath_ctxt;
392   if(current_node) {
393     current_ctxt = xmlXPathNewContext(master_config);
394     current_ctxt->node = current_node;
395   }
396   if(path[0] == '/')
397     strlcpy(xpath_expr, path, sizeof(xpath_expr));
398   else
399     snprintf(xpath_expr, sizeof(xpath_expr),
400              "ancestor-or-self::node()/%s", path);
401   pobj = xmlXPathEval((xmlChar *)xpath_expr, current_ctxt);
402   if(!pobj) goto out;
403   if(pobj->type != XPATH_NODESET) goto out;
404   if(xmlXPathNodeSetIsEmpty(pobj->nodesetval)) goto out;
405   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
406   /* These are in the order of root to leaf
407    * We want to recurse... apply:
408    *   1. our parent's config
409    *   2. our "inherit" config if it exists.
410    *   3. our config.
411    */
412   node = xmlXPathNodeSetItem(pobj->nodesetval, cnt-1);
413   /* 1. */
414   if(cnt > 1) {
415     parent_node = xmlXPathNodeSetItem(pobj->nodesetval, cnt-2);
416     if(parent_node != current_node)
417       noit_conf_get_into_hash(parent_node, (const char *)node->name, table);
418   }
419   /* 2. */
420   inheritid = (char *)xmlGetProp(node, (xmlChar *)"inherit");
421   if(inheritid) {
422     snprintf(xpath_expr, sizeof(xpath_expr), "//*[@id=\"%s\"]", inheritid);
423     noit_conf_get_into_hash(NULL, xpath_expr, table);
424   }
425   /* 3. */
426   noit_conf_get_elements_into_hash(node, "*", table);
427
428  out:
429   if(pobj) xmlXPathFreeObject(pobj);
430   if(current_ctxt && current_ctxt != xpath_ctxt)
431     xmlXPathFreeContext(current_ctxt);
432 }
433 noit_hash_table *noit_conf_get_hash(noit_conf_section_t section,
434                                     const char *path) {
435   noit_hash_table *table = NULL;
436
437   table = calloc(1, sizeof(*table));
438   noit_conf_get_into_hash(section, path, table);
439   return table;
440 }
441 noit_conf_section_t noit_conf_get_section(noit_conf_section_t section,
442                                           const char *path) {
443   noit_conf_section_t subsection = NULL;
444   xmlXPathObjectPtr pobj = NULL;
445   xmlXPathContextPtr current_ctxt;
446   xmlNodePtr current_node = (xmlNodePtr)section;
447
448   current_ctxt = xpath_ctxt;
449   if(current_node) {
450     current_ctxt = xmlXPathNewContext(master_config);
451     current_ctxt->node = current_node;
452   }
453   pobj = xmlXPathEval((xmlChar *)path, current_ctxt);
454   if(!pobj) goto out;
455   if(pobj->type != XPATH_NODESET) goto out;
456   if(xmlXPathNodeSetIsEmpty(pobj->nodesetval)) goto out;
457   subsection = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
458  out:
459   if(pobj) xmlXPathFreeObject(pobj);
460   if(current_ctxt && current_ctxt != xpath_ctxt)
461     xmlXPathFreeContext(current_ctxt);
462   return subsection;
463 }
464 noit_conf_section_t *noit_conf_get_sections(noit_conf_section_t section,
465                                             const char *path,
466                                             int *cnt) {
467   int i;
468   noit_conf_section_t *sections = NULL;
469   xmlXPathObjectPtr pobj = NULL;
470   xmlXPathContextPtr current_ctxt;
471   xmlNodePtr current_node = (xmlNodePtr)section;
472
473   *cnt = 0;
474   current_ctxt = xpath_ctxt;
475   if(current_node) {
476     current_ctxt = xmlXPathNewContext(master_config);
477     current_ctxt->node = current_node;
478   }
479   pobj = xmlXPathEval((xmlChar *)path, current_ctxt);
480   if(!pobj) goto out;
481   if(pobj->type != XPATH_NODESET) goto out;
482   if(xmlXPathNodeSetIsEmpty(pobj->nodesetval)) goto out;
483   *cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
484   sections = calloc(*cnt, sizeof(*sections));
485   for(i=0; i<*cnt; i++)
486     sections[i] = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
487  out:
488   if(pobj) xmlXPathFreeObject(pobj);
489   if(current_ctxt && current_ctxt != xpath_ctxt)
490     xmlXPathFreeContext(current_ctxt);
491   return sections;
492 }
493 int _noit_conf_get_string(noit_conf_section_t section, xmlNodePtr *vnode,
494                           const char *path, char **value) {
495   const char *str;
496   int i, rv = 1;
497   xmlXPathObjectPtr pobj = NULL;
498   xmlXPathContextPtr current_ctxt;
499   xmlNodePtr current_node = (xmlNodePtr)section;
500
501   current_ctxt = xpath_ctxt;
502   if(current_node) {
503     current_ctxt = xmlXPathNewContext(master_config);
504     current_ctxt->node = current_node;
505   }
506   pobj = xmlXPathEval((xmlChar *)path, current_ctxt);
507   if(pobj) {
508     xmlNodePtr node;
509     switch(pobj->type) {
510       case XPATH_NODESET:
511         if(xmlXPathNodeSetIsEmpty(pobj->nodesetval)) goto fallback;
512         i = xmlXPathNodeSetGetLength(pobj->nodesetval);
513         node = xmlXPathNodeSetItem(pobj->nodesetval, i-1);
514         if(vnode) *vnode = node;
515         *value = (char *)xmlXPathCastNodeToString(node);
516         break;
517       default:
518         *value = (char *)xmlXPathCastToString(pobj);
519     }
520     goto found;
521   }
522  fallback:
523   if(noit_hash_retr_str(&_compiled_fallback,
524                         path, strlen(path), &str)) {
525     *value = (char *)xmlStrdup((xmlChar *)str);
526     goto found;
527   }
528   rv = 0;
529  found:
530   if(pobj) xmlXPathFreeObject(pobj);
531   if(current_ctxt && current_ctxt != xpath_ctxt)
532     xmlXPathFreeContext(current_ctxt);
533   return rv;
534 }
535 int noit_conf_get_uuid(noit_conf_section_t section,
536                        const char *path, uuid_t out) {
537   char *str;
538   if(_noit_conf_get_string(section,NULL,path,&str)) {
539     if(uuid_parse(str, out) == 0) return 1;
540     return 0;
541   }
542   return 0;
543 }
544 int noit_conf_get_string(noit_conf_section_t section,
545                          const char *path, char **value) {
546   char *str;
547   if(_noit_conf_get_string(section,NULL,path,&str)) {
548     *value = strdup(str);
549     xmlFree(str);
550     return 1;
551   }
552   return 0;
553 }
554 int noit_conf_get_stringbuf(noit_conf_section_t section,
555                             const char *path, char *buf, int len) {
556   char *str;
557   if(_noit_conf_get_string(section,NULL,path,&str)) {
558     strlcpy(buf, str, len);
559     xmlFree(str);
560     return 1;
561   }
562   return 0;
563 }
564 int noit_conf_set_string(noit_conf_section_t section,
565                          const char *path, const char *value) {
566   noit_hash_replace(&_tmp_config,
567                     strdup(path), strlen(path), (void *)strdup(value),
568                     free, free);
569   return 1;
570 }
571 int noit_conf_string_to_int(const char *str) {
572   int base = 10;
573   if(!str) return 0;
574   if(str[0] == '0') {
575     if(str[1] == 'x') base = 16;
576     else base = 8;
577   }
578   return strtol(str, NULL, base);
579 }
580 int noit_conf_get_int(noit_conf_section_t section,
581                       const char *path, int *value) {
582   char *str;
583   if(_noit_conf_get_string(section,NULL,path,&str)) {
584     *value = (int)noit_conf_string_to_int(str);
585     xmlFree(str);
586     return 1;
587   }
588   return 0;
589 }
590 int noit_conf_set_int(noit_conf_section_t section,
591                       const char *path, int value) {
592   char buffer[32];
593   snprintf(buffer, 32, "%d", value);
594   return noit_conf_set_string(section,path,buffer);
595 }
596 float noit_conf_string_to_float(const char *str) {
597   if(!str) return 0.0;
598   return atof(str);
599 }
600 int noit_conf_get_float(noit_conf_section_t section,
601                         const char *path, float *value) {
602   char *str;
603   if(_noit_conf_get_string(section,NULL,path,&str)) {
604     *value = noit_conf_string_to_float(str);
605     xmlFree(str);
606     return 1;
607   }
608   return 0;
609 }
610 int noit_conf_set_float(noit_conf_section_t section,
611                         const char *path, float value) {
612   char buffer[32];
613   snprintf(buffer, 32, "%f", value);
614   return noit_conf_set_string(section,path,buffer);
615 }
616 noit_boolean noit_conf_string_to_boolean(const char *str) {
617   if(!str) return noit_false;
618   if(!strcasecmp(str, "true") || !strcasecmp(str, "on")) return noit_true;
619   return noit_false;
620 }
621 int noit_conf_get_boolean(noit_conf_section_t section,
622                           const char *path, noit_boolean *value) {
623   char *str;
624   if(_noit_conf_get_string(section,NULL,path,&str)) {
625     *value = noit_conf_string_to_boolean(str);
626     xmlFree(str);
627     return 1;
628   }
629   return 0;
630 }
631 int noit_conf_set_boolean(noit_conf_section_t section,
632                           const char *path, noit_boolean value) {
633   if(value == noit_true)
634     return noit_conf_set_string(section,path,"true");
635   return noit_conf_set_string(section,path,"false");
636 }
637
638 struct config_line_vstr {
639   char *buff;
640   int raw_len;
641   int len;
642   int allocd;
643   enum { CONFIG_RAW = 0, CONFIG_COMPRESSED, CONFIG_B64 } target, encoded;
644 };
645 static int
646 noit_config_log_write_xml(void *vstr, const char *buffer, int len) {
647   struct config_line_vstr *clv = vstr;
648   assert(clv->encoded == CONFIG_RAW);
649   if(!clv->buff) {
650     clv->allocd = 8192;
651     clv->buff = malloc(clv->allocd);
652   }
653   while(len + clv->len > clv->allocd) {
654     char *newbuff;
655     int newsize = clv->allocd;
656     newsize <<= 1;
657     newbuff = realloc(clv->buff, newsize);
658     if(!newbuff) {
659       return -1;
660     }
661     clv->allocd = newsize;
662     clv->buff = newbuff;
663   }
664   memcpy(clv->buff + clv->len, buffer, len);
665   clv->len += len;
666   return len;
667 }
668 static int
669 noit_config_log_close_xml(void *vstr) {
670   struct config_line_vstr *clv = vstr;
671   uLong initial_dlen, dlen;
672   char *compbuff, *b64buff;
673
674   if(clv->buff == NULL) {
675     clv->encoded = clv->target;
676     return 0;
677   }
678   clv->raw_len = clv->len;
679   assert(clv->encoded == CONFIG_RAW);
680   if(clv->encoded == clv->target) return 0;
681
682   /* Compress */
683   initial_dlen = dlen = compressBound(clv->len);
684   compbuff = malloc(initial_dlen);
685   if(!compbuff) return -1;
686   if(Z_OK != compress2((Bytef *)compbuff, &dlen,
687                        (Bytef *)clv->buff, clv->len, 9)) {
688     noitL(noit_error, "Error compressing config for transmission.\n");
689     free(compbuff);
690     return -1;
691   }
692   free(clv->buff);
693   clv->buff = compbuff;
694   clv->allocd = initial_dlen;
695   clv->len = dlen;
696   clv->encoded = CONFIG_COMPRESSED;
697   if(clv->encoded == clv->target) return 0;
698
699   /* Encode */
700   initial_dlen = ((clv->len + 2) / 3) * 4;
701   b64buff = malloc(initial_dlen);
702   dlen = noit_b64_encode((unsigned char *)clv->buff, clv->len,
703                          b64buff, initial_dlen);
704   if(dlen == 0) {
705     free(b64buff);
706     return -1;
707   }
708   free(clv->buff);
709   clv->buff = b64buff;
710   clv->allocd = initial_dlen;
711   clv->len = dlen;
712   clv->encoded = CONFIG_B64;
713   if(clv->encoded == clv->target) return 0;
714   return -1;
715 }
716
717 int
718 noit_conf_reload(noit_console_closure_t ncct,
719                  int argc, char **argv,
720                  noit_console_state_t *state, void *closure) {
721   if(noit_conf_load(master_config_file)) {
722     nc_printf(ncct, "error loading config\n");
723     return -1;
724   }
725   return 0;
726 }
727 int
728 noit_conf_write_terminal(noit_console_closure_t ncct,
729                          int argc, char **argv,
730                          noit_console_state_t *state, void *closure) {
731   xmlOutputBufferPtr out;
732   xmlCharEncodingHandlerPtr enc;
733   enc = xmlGetCharEncodingHandler(XML_CHAR_ENCODING_UTF8);
734   out = xmlOutputBufferCreateIO(noit_console_write_xml,
735                                 noit_console_close_xml,
736                                 ncct, enc);
737   noit_conf_kansas_city_shuffle_undo(master_config);
738   xmlSaveFormatFileTo(out, master_config, "utf8", 1);
739   noit_conf_kansas_city_shuffle_redo(master_config);
740   return 0;
741 }
742 int
743 noit_conf_write_file_console(noit_console_closure_t ncct,
744                              int argc, char **argv,
745                              noit_console_state_t *state, void *closure) {
746   int rv;
747   char *err = NULL;
748   rv = noit_conf_write_file(&err);
749   nc_printf(ncct, "%s\n", err);
750   if(err) free(err);
751   return rv;
752 }
753 int
754 noit_conf_write_file(char **err) {
755   int fd, len;
756   char master_file_tmp[PATH_MAX];
757   char errstr[1024];
758   xmlOutputBufferPtr out;
759   xmlCharEncodingHandlerPtr enc;
760
761   snprintf(master_file_tmp, sizeof(master_file_tmp),
762            "%s.tmp", master_config_file);
763   unlink(master_file_tmp);
764   fd = open(master_file_tmp, O_CREAT|O_EXCL|O_WRONLY, 0640);
765   if(fd < 0) {
766     snprintf(errstr, sizeof(errstr), "Failed to open tmp file: %s",
767              strerror(errno));
768     if(err) *err = strdup(errstr);
769     return -1;
770   }
771   enc = xmlGetCharEncodingHandler(XML_CHAR_ENCODING_UTF8);
772   out = xmlOutputBufferCreateFd(fd, enc);
773   if(!out) {
774     close(fd);
775     unlink(master_file_tmp);
776     if(err) *err = strdup("internal error: OutputBufferCreate failed");
777     return -1;
778   }
779   noit_conf_kansas_city_shuffle_undo(master_config);
780   len = xmlSaveFormatFileTo(out, master_config, "utf8", 1);
781   noit_conf_kansas_city_shuffle_redo(master_config);
782   close(fd);
783   if(len <= 0) {
784     if(err) *err = strdup("internal error: writing to tmp file failed.");
785     return -1;
786   }
787   if(rename(master_file_tmp, master_config_file) != 0) {
788     snprintf(errstr, sizeof(errstr), "Failed to replace file: %s",
789              strerror(errno));
790     if(err) *err = strdup(errstr);
791     return -1;
792   }
793   snprintf(errstr, sizeof(errstr), "%d bytes written.", len);
794   if(err) *err = strdup(errstr);
795   return 0;
796 }
797 char *
798 noit_conf_xml_in_mem(size_t *len) {
799   struct config_line_vstr *clv;
800   xmlOutputBufferPtr out;
801   xmlCharEncodingHandlerPtr enc;
802   char *rv;
803
804   clv = calloc(1, sizeof(*clv));
805   clv->target = CONFIG_RAW;
806   enc = xmlGetCharEncodingHandler(XML_CHAR_ENCODING_UTF8);
807   out = xmlOutputBufferCreateIO(noit_config_log_write_xml,
808                                 noit_config_log_close_xml,
809                                 clv, enc);
810   noit_conf_kansas_city_shuffle_undo(master_config);
811   xmlSaveFormatFileTo(out, master_config, "utf8", 1);
812   noit_conf_kansas_city_shuffle_redo(master_config);
813   if(clv->encoded != CONFIG_RAW) {
814     noitL(noit_error, "Error logging configuration\n");
815     if(clv->buff) free(clv->buff);
816     free(clv);
817     return NULL;
818   }
819   rv = clv->buff;
820   *len = clv->len;
821   free(clv);
822   return rv;
823 }
824
825 int
826 noit_conf_write_log() {
827   static u_int32_t last_write_gen = 0;
828   static noit_log_stream_t config_log = NULL;
829   struct timeval __now;
830   xmlOutputBufferPtr out;
831   xmlCharEncodingHandlerPtr enc;
832   struct config_line_vstr *clv;
833   SETUP_LOG(config, return -1);
834
835   /* We know we haven't changed */
836   if(last_write_gen == __config_gen) return 0;
837
838   gettimeofday(&__now, NULL);
839   clv = calloc(1, sizeof(*clv));
840   clv->target = CONFIG_B64;
841   enc = xmlGetCharEncodingHandler(XML_CHAR_ENCODING_UTF8);
842   out = xmlOutputBufferCreateIO(noit_config_log_write_xml,
843                                 noit_config_log_close_xml,
844                                 clv, enc);
845   noit_conf_kansas_city_shuffle_undo(master_config);
846   xmlSaveFormatFileTo(out, master_config, "utf8", 1);
847   noit_conf_kansas_city_shuffle_redo(master_config);
848   if(clv->encoded != CONFIG_B64) {
849     noitL(noit_error, "Error logging configuration\n");
850     if(clv->buff) free(clv->buff);
851     free(clv);
852     return -1;
853   }
854   noitL(config_log, "n\t%lu.%03lu\t%d\t%.*s\n",
855         __now.tv_sec, __now.tv_usec / 1000UL, clv->raw_len,
856         clv->len, clv->buff);
857   free(clv->buff);
858   free(clv);
859   last_write_gen = __config_gen;
860   return 0;
861 }
862
863 void
864 noit_conf_log_init(const char *toplevel) {
865   int i, cnt = 0, o, ocnt = 0;
866   noit_conf_section_t *log_configs, *outlets;
867   char path[256];
868
869   snprintf(path, sizeof(path), "/%s/logs//log", toplevel);
870   log_configs = noit_conf_get_sections(NULL, path, &cnt);
871   noitL(noit_stderr, "Found %d %s stanzas\n", cnt, path);
872   for(i=0; i<cnt; i++) {
873     noit_log_stream_t ls;
874     char name[256], type[256], path[256];
875     noit_hash_table *config;
876     noit_boolean disabled;
877     noit_boolean debug;
878
879     if(!noit_conf_get_stringbuf(log_configs[i],
880                                 "ancestor-or-self::node()/@name",
881                                 name, sizeof(name))) {
882       noitL(noit_error, "log section %d does not have a name attribute\n", i+1);
883       exit(-1);
884     }
885     if(!noit_conf_get_stringbuf(log_configs[i],
886                                 "ancestor-or-self::node()/@type",
887                                 type, sizeof(type))) {
888       type[0] = '\0';
889     }
890     if(!noit_conf_get_stringbuf(log_configs[i],
891                                 "ancestor-or-self::node()/@path",
892                                 path, sizeof(path))) {
893       path[0] = '\0';
894     }
895     config = noit_conf_get_hash(log_configs[i],
896                                 "ancestor-or-self::node()/config/*");
897     ls = noit_log_stream_new(name, type[0] ? type : NULL,
898                              path[0] ? path : NULL, NULL, config);
899     if(!ls) {
900       fprintf(stderr, "Error configuring log: %s[%s:%s]\n", name, type, path);
901       exit(-1);
902     }
903
904     if(noit_conf_get_boolean(log_configs[i],
905                              "ancestor-or-self::node()/@disabled",
906                              &disabled) && disabled)
907       ls->enabled = 0;
908      
909     if(noit_conf_get_boolean(log_configs[i],
910                              "ancestor-or-self::node()/@debug",
911                              &debug) && debug)
912       ls->debug = 1;
913      
914     outlets = noit_conf_get_sections(log_configs[i],
915                                      "ancestor-or-self::node()/outlet", &ocnt);
916     noitL(noit_error, "Found %d outlets for log '%s'\n", ocnt, name);
917
918     for(o=0; o<ocnt; o++) {
919       noit_log_stream_t outlet;
920       char oname[256];
921       noit_conf_get_stringbuf(outlets[o], "@name",
922                               oname, sizeof(oname));
923       outlet = noit_log_stream_find(oname);
924       if(!outlet) {
925         fprintf(stderr, "Cannot find outlet '%s' for %s[%s:%s]\n", oname,
926               name, type, path);
927         exit(-1);
928       }
929       else
930         noit_log_stream_add_stream(ls, outlet);
931     }
932     if(outlets) free(outlets);
933   }
934   if(log_configs) free(log_configs);
935 }
936
937 static void
938 conf_t_userdata_free(void *data) {
939   noit_conf_t_userdata_t *info = data;
940   if(info) {
941     if(info->path) free(info->path);
942     free(info);
943   }
944 }
945
946 static int
947 noit_console_state_conf_terminal(noit_console_closure_t ncct,
948                                  int argc, char **argv,
949                                  noit_console_state_t *state, void *closure) {
950   noit_conf_t_userdata_t *info;
951   if(argc) {
952     nc_printf(ncct, "extra arguments not expected.\n");
953     return -1;
954   }
955   info = calloc(1, sizeof(*info));
956   info->path = strdup("/");
957   noit_console_userdata_set(ncct, NOIT_CONF_T_USERDATA, info,
958                             conf_t_userdata_free);
959   noit_console_state_push_state(ncct, state);
960   noit_console_state_init(ncct);
961   return 0;
962 }
963 static int
964 noit_console_config_section(noit_console_closure_t ncct,
965                             int argc, char **argv,
966                             noit_console_state_t *state, void *closure) {
967   const char *err = "internal error";
968   char *path, xpath[1024];
969   noit_conf_t_userdata_t *info;
970   xmlXPathObjectPtr pobj = NULL;
971   xmlXPathContextPtr xpath_ctxt = NULL;
972   xmlNodePtr node = NULL, newnode;
973   vpsized_int delete = (vpsized_int)closure;
974
975   noit_conf_xml_xpath(NULL, &xpath_ctxt);
976   if(argc != 1) {
977     nc_printf(ncct, "requires one argument\n");
978     return -1;
979   }
980   if(strchr(argv[0], '/')) {
981     nc_printf(ncct, "invalid section name\n");
982     return -1;
983   }
984   if(!strcmp(argv[0], "check") ||
985      !strcmp(argv[0], "noit") ||
986      !strcmp(argv[0], "filterset") ||
987      !strcmp(argv[0], "config")) {
988     nc_printf(ncct, "%s is reserved.\n", argv[0]);
989     return -1;
990   }
991   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
992   if(!strcmp(info->path, "/")) {
993     nc_printf(ncct, "manipulation of toplevel section disallowed\n");
994     return -1;
995   }
996
997   if(delete) {
998     /* We cannot delete if we have checks */
999     snprintf(xpath, sizeof(xpath), "/%s%s/%s//check", root_node_name,
1000              info->path, argv[0]);
1001     pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
1002     if(!pobj || pobj->type != XPATH_NODESET ||
1003        !xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
1004       err = "cannot delete section, has checks";
1005       goto bad;
1006     }
1007     if(pobj) xmlXPathFreeObject(pobj);
1008   }
1009
1010   snprintf(xpath, sizeof(xpath), "/%s%s/%s", root_node_name,
1011            info->path, argv[0]);
1012   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
1013   if(!pobj || pobj->type != XPATH_NODESET) {
1014     err = "internal error: cannot detect section";
1015     goto bad;
1016   }
1017   if(!delete && !xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
1018     if(xmlXPathNodeSetGetLength(pobj->nodesetval) == 1) {
1019       node = xmlXPathNodeSetItem(pobj->nodesetval, 0);
1020       if(info->path) free(info->path);
1021       info->path = strdup((char *)xmlGetNodePath(node) +
1022                           1 + strlen(root_node_name));
1023       goto cdout;
1024     }
1025     err = "cannot create section";
1026     goto bad;
1027   }
1028   if(delete && xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
1029     err = "no such section";
1030     goto bad;
1031   }
1032   if(delete) {
1033     node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
1034     xmlUnlinkNode(node);
1035     noit_conf_mark_changed();
1036     return 0;
1037   }
1038   if(pobj) xmlXPathFreeObject(pobj);
1039   pobj = NULL;
1040
1041   if(!strcmp(argv[0],"include")) {
1042     err = "include is a reserved section name";
1043     goto bad;
1044   }
1045   path = strcmp(info->path, "/") ? info->path : "";
1046   snprintf(xpath, sizeof(xpath), "/%s%s", root_node_name, path);
1047   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
1048   if(!pobj || pobj->type != XPATH_NODESET ||
1049      xmlXPathNodeSetGetLength(pobj->nodesetval) != 1) {
1050     err = "path invalid?";
1051     goto bad;
1052   }
1053   node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
1054   if((newnode = xmlNewChild(node, NULL, (xmlChar *)argv[0], NULL)) != NULL) {
1055     noit_conf_mark_changed();
1056     if(info->path) free(info->path);
1057     info->path = strdup((char *)xmlGetNodePath(newnode) + 1 +
1058                         strlen(root_node_name));
1059   }
1060   else {
1061     err = "failed to create section";
1062     goto bad;
1063   }
1064  cdout:
1065   if(pobj) xmlXPathFreeObject(pobj);
1066   return 0;
1067  bad:
1068   if(pobj) xmlXPathFreeObject(pobj);
1069   nc_printf(ncct, "%s\n", err);
1070   return -1;
1071 }
1072
1073 int
1074 noit_console_generic_show(noit_console_closure_t ncct,
1075                           int argc, char **argv,
1076                           noit_console_state_t *state, void *closure) {
1077   int i, cnt, titled = 0, cliplen = 0;
1078   const char *path = "", *basepath = NULL;
1079   char xpath[1024];
1080   noit_conf_t_userdata_t *info = NULL;
1081   xmlXPathObjectPtr pobj = NULL;
1082   xmlXPathContextPtr xpath_ctxt = NULL, current_ctxt;
1083   xmlDocPtr master_config = NULL;
1084   xmlNodePtr node = NULL;
1085
1086   noit_conf_xml_xpath(&master_config, &xpath_ctxt);
1087   if(argc > 1) {
1088     nc_printf(ncct, "too many arguments\n");
1089     return -1;
1090   }
1091
1092   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
1093   if(info) path = basepath = info->path;
1094   if(!info && argc == 0) {
1095     nc_printf(ncct, "argument required when not in configuration mode\n");
1096     return -1;
1097   }
1098
1099   if(argc == 1) path = argv[0];
1100   if(!basepath) basepath = path;
1101
1102   /* { / } is a special case */
1103   if(!strcmp(basepath, "/")) basepath = "";
1104   if(!strcmp(path, "/")) path = "";
1105
1106   if(!master_config) {
1107     nc_printf(ncct, "no config\n");
1108     return -1;
1109   }
1110
1111   /* { / } is the only path that will end with a /
1112    * in XPath { / / * } means something _entirely different than { / * }
1113    * Ever notice how it is hard to describe xpath in C comments?
1114    */
1115   /* We don't want to show the root node */
1116   cliplen = strlen(root_node_name) + 2; /* /name/ */
1117
1118   /* If we are in configuration mode
1119    * and we are without an argument or the argument is absolute,
1120    * clip the current path off */
1121   if(info && (argc == 0 || path[0] != '/')) cliplen += strlen(basepath);
1122   if(!path[0] || path[0] == '/') /* base only, or absolute path requested */
1123     snprintf(xpath, sizeof(xpath), "/%s%s/@*", root_node_name, path);
1124   else
1125     snprintf(xpath, sizeof(xpath), "/%s%s/%s/@*", root_node_name,
1126              basepath, path);
1127
1128   current_ctxt = xpath_ctxt;
1129   pobj = xmlXPathEval((xmlChar *)xpath, current_ctxt);
1130   if(!pobj || pobj->type != XPATH_NODESET) {
1131     nc_printf(ncct, "no such object\n");
1132     goto bad;
1133   }
1134   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
1135   titled = 0;
1136   for(i=0; i<cnt; i++) {
1137     node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
1138     if(node->children && node->children == xmlGetLastChild(node) &&
1139       xmlNodeIsText(node->children)) {
1140       if(!titled++) nc_printf(ncct, "== Section Settings ==\n");
1141       nc_printf(ncct, "%s: %s\n", xmlGetNodePath(node) + cliplen,
1142                 xmlXPathCastNodeToString(node->children));
1143     }
1144   }
1145   xmlXPathFreeObject(pobj);
1146
1147   /* _shorten string_ turning last { / @ * } to { / * } */
1148   if(!path[0] || path[0] == '/') /* base only, or absolute path requested */
1149     snprintf(xpath, sizeof(xpath), "/%s%s/*", root_node_name, path);
1150   else
1151     snprintf(xpath, sizeof(xpath), "/%s%s/%s/*",
1152              root_node_name, basepath, path);
1153   pobj = xmlXPathEval((xmlChar *)xpath, current_ctxt);
1154   if(!pobj || pobj->type != XPATH_NODESET) {
1155     nc_printf(ncct, "no such object\n");
1156     goto bad;
1157   }
1158   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
1159   titled = 0;
1160   for(i=0; i<cnt; i++) {
1161     node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
1162     if(!(node->children && node->children == xmlGetLastChild(node) &&
1163          xmlNodeIsText(node->children))) {
1164       if(!titled++) nc_printf(ncct, "== Subsections ==\n");
1165       nc_printf(ncct, "%s\n", xmlGetNodePath(node) + cliplen);
1166     }
1167   }
1168   xmlXPathFreeObject(pobj);
1169   return 0;
1170  bad:
1171   if(pobj) xmlXPathFreeObject(pobj);
1172   return -1;
1173 }
1174 int
1175 noit_console_config_cd(noit_console_closure_t ncct,
1176                        int argc, char **argv,
1177                        noit_console_state_t *state, void *closure) {
1178   const char *err = "internal error";
1179   char *path, xpath[1024];
1180   noit_conf_t_userdata_t *info;
1181   xmlXPathObjectPtr pobj = NULL;
1182   xmlXPathContextPtr xpath_ctxt = NULL, current_ctxt;
1183   xmlNodePtr node = NULL;
1184   char *dest;
1185
1186   noit_conf_xml_xpath(NULL, &xpath_ctxt);
1187   if(argc != 1 && !closure) {
1188     nc_printf(ncct, "requires one argument\n");
1189     return -1;
1190   }
1191   dest = argc ? argv[0] : (char *)closure;
1192   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
1193   if(dest[0] == '/')
1194     snprintf(xpath, sizeof(xpath), "/%s%s", root_node_name, dest);
1195   else {
1196     snprintf(xpath, sizeof(xpath), "/%s%s/%s", root_node_name,
1197              info->path, dest);
1198   }
1199   if(xpath[strlen(xpath)-1] == '/') xpath[strlen(xpath)-1] = '\0';
1200
1201   current_ctxt = xpath_ctxt;
1202   pobj = xmlXPathEval((xmlChar *)xpath, current_ctxt);
1203   if(!pobj || pobj->type != XPATH_NODESET ||
1204      xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
1205     err = "no such section";
1206     goto bad;
1207   }
1208   if(xmlXPathNodeSetGetLength(pobj->nodesetval) > 1) {
1209     err = "ambiguous section";
1210     goto bad;
1211   }
1212
1213   node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
1214   if(!strcmp((char *)node->name, "check") ||
1215      !strcmp((char *)node->name, "noit") ||
1216      !strcmp((char *)node->name, "filterset") ||
1217      !strcmp((char *)node->name, "config")) {
1218     err = "reserved word";
1219     goto bad;
1220   }
1221   path = (char *)xmlGetNodePath(node);
1222   if(strlen(path) < strlen(root_node_name) + 1 ||
1223      strncmp(path + 1, root_node_name, strlen(root_node_name)) ||
1224      (path[strlen(root_node_name) + 1] != '/' &&
1225       path[strlen(root_node_name) + 1] != '\0')) {
1226     err = "new path outside out tree";
1227     goto bad;
1228   }
1229   free(info->path);
1230   if(!strcmp(path + 1, root_node_name))
1231     info->path = strdup("/");
1232   else
1233     info->path = strdup((char *)xmlGetNodePath(node) + 1 +
1234                         strlen(root_node_name));
1235   if(pobj) xmlXPathFreeObject(pobj);
1236   if(closure) noit_console_state_pop(ncct, argc, argv, NULL, NULL);
1237   return 0;
1238  bad:
1239   if(pobj) xmlXPathFreeObject(pobj);
1240   nc_printf(ncct, "%s [%s]\n", err, xpath);
1241   return -1;
1242 }
1243
1244 char *
1245 conf_t_prompt(EditLine *el) {
1246   noit_console_closure_t ncct;
1247   noit_conf_t_userdata_t *info;
1248   static char *tl = "noit(conf)# ";
1249   static char *pfmt = "noit(conf:%s%s)# ";
1250   int path_len, max_len;
1251
1252   el_get(el, EL_USERDATA, (void *)&ncct);
1253   if(!ncct) return tl;
1254   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
1255   if(!info) return tl;
1256
1257   path_len = strlen(info->path);
1258   max_len = sizeof(info->prompt) - (strlen(pfmt) - 4 /* %s%s */) - 1 /* \0 */;
1259   if(path_len > max_len)
1260     snprintf(info->prompt, sizeof(info->prompt),
1261              pfmt, "...", info->path + path_len - max_len + 3 /* ... */);
1262   else
1263     snprintf(info->prompt, sizeof(info->prompt), pfmt, "", info->path);
1264   return info->prompt;
1265 }
1266
1267 #define NEW_STATE(a) (a) = noit_console_state_alloc()
1268 #define ADD_CMD(a,cmd,func,ac,ss,c) \
1269   noit_console_state_add_cmd((a), \
1270     NCSCMD(cmd, func, ac, ss, c))
1271 #define DELEGATE_CMD(a,cmd,ac,ss) \
1272   noit_console_state_add_cmd((a), \
1273     NCSCMD(cmd, noit_console_state_delegate, ac, ss, NULL))
1274
1275 void noit_console_conf_init() {
1276   noit_console_state_t *tl, *_conf_state, *_conf_t_state,
1277                        *_write_state, *_unset_state;
1278
1279   tl = noit_console_state_initial();
1280
1281   /* write <terimal|memory|file> */
1282   NEW_STATE(_write_state);
1283   ADD_CMD(_write_state, "terminal", noit_conf_write_terminal, NULL, NULL, NULL);
1284   ADD_CMD(_write_state, "file", noit_conf_write_file_console, NULL, NULL, NULL);
1285   /* write memory?  It's to a file, but I like router syntax */
1286   ADD_CMD(_write_state, "memory", noit_conf_write_file_console, NULL, NULL, NULL);
1287
1288   NEW_STATE(_unset_state);
1289   ADD_CMD(_unset_state, "section",
1290           noit_console_config_section, NULL, NULL, (void *)1);
1291
1292   NEW_STATE(_conf_t_state);
1293   _conf_t_state->console_prompt_function = conf_t_prompt;
1294   noit_console_state_add_cmd(_conf_t_state, &console_command_exit);
1295
1296   ADD_CMD(_conf_t_state, "ls", noit_console_generic_show, NULL, NULL, NULL);
1297   ADD_CMD(_conf_t_state, "cd", noit_console_config_cd, NULL, NULL, NULL);
1298   ADD_CMD(_conf_t_state, "section",
1299           noit_console_config_section, NULL, NULL, (void *)0);
1300
1301   DELEGATE_CMD(_conf_t_state, "write",
1302                noit_console_opt_delegate, _write_state);
1303   DELEGATE_CMD(_conf_t_state, "no", noit_console_opt_delegate, _unset_state);
1304
1305   NEW_STATE(_conf_state);
1306   ADD_CMD(_conf_state, "terminal",
1307           noit_console_state_conf_terminal, NULL, _conf_t_state, NULL);
1308
1309   ADD_CMD(tl, "configure",
1310           noit_console_state_delegate, noit_console_opt_delegate,
1311           _conf_state, NULL);
1312   ADD_CMD(tl, "write",
1313           noit_console_state_delegate, noit_console_opt_delegate,
1314           _write_state, NULL);
1315 }
1316
Note: See TracBrowser for help on using the browser.