root/src/noit_conf.c

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

cleanup some logging

  • 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   struct stat st;
761   mode_t mode = 0640; /* the default */
762
763   if(stat(master_config_file, &st) == 0)
764     mode = st.st_mode;
765   snprintf(master_file_tmp, sizeof(master_file_tmp),
766            "%s.tmp", master_config_file);
767   unlink(master_file_tmp);
768   fd = open(master_file_tmp, O_CREAT|O_EXCL|O_WRONLY, mode);
769   if(fd < 0) {
770     snprintf(errstr, sizeof(errstr), "Failed to open tmp file: %s",
771              strerror(errno));
772     if(err) *err = strdup(errstr);
773     return -1;
774   }
775   enc = xmlGetCharEncodingHandler(XML_CHAR_ENCODING_UTF8);
776   out = xmlOutputBufferCreateFd(fd, enc);
777   if(!out) {
778     close(fd);
779     unlink(master_file_tmp);
780     if(err) *err = strdup("internal error: OutputBufferCreate failed");
781     return -1;
782   }
783   noit_conf_kansas_city_shuffle_undo(master_config);
784   len = xmlSaveFormatFileTo(out, master_config, "utf8", 1);
785   noit_conf_kansas_city_shuffle_redo(master_config);
786   close(fd);
787   if(len <= 0) {
788     if(err) *err = strdup("internal error: writing to tmp file failed.");
789     return -1;
790   }
791   if(rename(master_file_tmp, master_config_file) != 0) {
792     snprintf(errstr, sizeof(errstr), "Failed to replace file: %s",
793              strerror(errno));
794     if(err) *err = strdup(errstr);
795     return -1;
796   }
797   snprintf(errstr, sizeof(errstr), "%d bytes written.", len);
798   if(err) *err = strdup(errstr);
799   return 0;
800 }
801 char *
802 noit_conf_xml_in_mem(size_t *len) {
803   struct config_line_vstr *clv;
804   xmlOutputBufferPtr out;
805   xmlCharEncodingHandlerPtr enc;
806   char *rv;
807
808   clv = calloc(1, sizeof(*clv));
809   clv->target = CONFIG_RAW;
810   enc = xmlGetCharEncodingHandler(XML_CHAR_ENCODING_UTF8);
811   out = xmlOutputBufferCreateIO(noit_config_log_write_xml,
812                                 noit_config_log_close_xml,
813                                 clv, enc);
814   noit_conf_kansas_city_shuffle_undo(master_config);
815   xmlSaveFormatFileTo(out, master_config, "utf8", 1);
816   noit_conf_kansas_city_shuffle_redo(master_config);
817   if(clv->encoded != CONFIG_RAW) {
818     noitL(noit_error, "Error logging configuration\n");
819     if(clv->buff) free(clv->buff);
820     free(clv);
821     return NULL;
822   }
823   rv = clv->buff;
824   *len = clv->len;
825   free(clv);
826   return rv;
827 }
828
829 int
830 noit_conf_write_log() {
831   static u_int32_t last_write_gen = 0;
832   static noit_log_stream_t config_log = NULL;
833   struct timeval __now;
834   xmlOutputBufferPtr out;
835   xmlCharEncodingHandlerPtr enc;
836   struct config_line_vstr *clv;
837   SETUP_LOG(config, return -1);
838
839   /* We know we haven't changed */
840   if(last_write_gen == __config_gen) return 0;
841
842   gettimeofday(&__now, NULL);
843   clv = calloc(1, sizeof(*clv));
844   clv->target = CONFIG_B64;
845   enc = xmlGetCharEncodingHandler(XML_CHAR_ENCODING_UTF8);
846   out = xmlOutputBufferCreateIO(noit_config_log_write_xml,
847                                 noit_config_log_close_xml,
848                                 clv, enc);
849   noit_conf_kansas_city_shuffle_undo(master_config);
850   xmlSaveFormatFileTo(out, master_config, "utf8", 1);
851   noit_conf_kansas_city_shuffle_redo(master_config);
852   if(clv->encoded != CONFIG_B64) {
853     noitL(noit_error, "Error logging configuration\n");
854     if(clv->buff) free(clv->buff);
855     free(clv);
856     return -1;
857   }
858   noitL(config_log, "n\t%lu.%03lu\t%d\t%.*s\n",
859         __now.tv_sec, __now.tv_usec / 1000UL, clv->raw_len,
860         clv->len, clv->buff);
861   free(clv->buff);
862   free(clv);
863   last_write_gen = __config_gen;
864   return 0;
865 }
866
867 void
868 noit_conf_log_init(const char *toplevel) {
869   int i, cnt = 0, o, ocnt = 0;
870   noit_conf_section_t *log_configs, *outlets;
871   char path[256];
872
873   snprintf(path, sizeof(path), "/%s/logs//log", toplevel);
874   log_configs = noit_conf_get_sections(NULL, path, &cnt);
875   noitL(noit_debug, "Found %d %s stanzas\n", cnt, path);
876   for(i=0; i<cnt; i++) {
877     noit_log_stream_t ls;
878     char name[256], type[256], path[256];
879     noit_hash_table *config;
880     noit_boolean disabled;
881     noit_boolean debug;
882
883     if(!noit_conf_get_stringbuf(log_configs[i],
884                                 "ancestor-or-self::node()/@name",
885                                 name, sizeof(name))) {
886       noitL(noit_error, "log section %d does not have a name attribute\n", i+1);
887       exit(-1);
888     }
889     if(!noit_conf_get_stringbuf(log_configs[i],
890                                 "ancestor-or-self::node()/@type",
891                                 type, sizeof(type))) {
892       type[0] = '\0';
893     }
894     if(!noit_conf_get_stringbuf(log_configs[i],
895                                 "ancestor-or-self::node()/@path",
896                                 path, sizeof(path))) {
897       path[0] = '\0';
898     }
899     config = noit_conf_get_hash(log_configs[i],
900                                 "ancestor-or-self::node()/config/*");
901     ls = noit_log_stream_new(name, type[0] ? type : NULL,
902                              path[0] ? path : NULL, NULL, config);
903     if(!ls) {
904       fprintf(stderr, "Error configuring log: %s[%s:%s]\n", name, type, path);
905       exit(-1);
906     }
907
908     if(noit_conf_get_boolean(log_configs[i],
909                              "ancestor-or-self::node()/@disabled",
910                              &disabled) && disabled)
911       ls->enabled = 0;
912      
913     if(noit_conf_get_boolean(log_configs[i],
914                              "ancestor-or-self::node()/@debug",
915                              &debug) && debug)
916       ls->debug = 1;
917      
918     outlets = noit_conf_get_sections(log_configs[i],
919                                      "ancestor-or-self::node()/outlet", &ocnt);
920     noitL(noit_debug, "Found %d outlets for log '%s'\n", ocnt, name);
921
922     for(o=0; o<ocnt; o++) {
923       noit_log_stream_t outlet;
924       char oname[256];
925       noit_conf_get_stringbuf(outlets[o], "@name",
926                               oname, sizeof(oname));
927       outlet = noit_log_stream_find(oname);
928       if(!outlet) {
929         fprintf(stderr, "Cannot find outlet '%s' for %s[%s:%s]\n", oname,
930               name, type, path);
931         exit(-1);
932       }
933       else
934         noit_log_stream_add_stream(ls, outlet);
935     }
936     if(outlets) free(outlets);
937   }
938   if(log_configs) free(log_configs);
939 }
940
941 static void
942 conf_t_userdata_free(void *data) {
943   noit_conf_t_userdata_t *info = data;
944   if(info) {
945     if(info->path) free(info->path);
946     free(info);
947   }
948 }
949
950 static int
951 noit_console_state_conf_terminal(noit_console_closure_t ncct,
952                                  int argc, char **argv,
953                                  noit_console_state_t *state, void *closure) {
954   noit_conf_t_userdata_t *info;
955   if(argc) {
956     nc_printf(ncct, "extra arguments not expected.\n");
957     return -1;
958   }
959   info = calloc(1, sizeof(*info));
960   info->path = strdup("/");
961   noit_console_userdata_set(ncct, NOIT_CONF_T_USERDATA, info,
962                             conf_t_userdata_free);
963   noit_console_state_push_state(ncct, state);
964   noit_console_state_init(ncct);
965   return 0;
966 }
967 static int
968 noit_console_config_section(noit_console_closure_t ncct,
969                             int argc, char **argv,
970                             noit_console_state_t *state, void *closure) {
971   const char *err = "internal error";
972   char *path, xpath[1024];
973   noit_conf_t_userdata_t *info;
974   xmlXPathObjectPtr pobj = NULL;
975   xmlXPathContextPtr xpath_ctxt = NULL;
976   xmlNodePtr node = NULL, newnode;
977   vpsized_int delete = (vpsized_int)closure;
978
979   noit_conf_xml_xpath(NULL, &xpath_ctxt);
980   if(argc != 1) {
981     nc_printf(ncct, "requires one argument\n");
982     return -1;
983   }
984   if(strchr(argv[0], '/')) {
985     nc_printf(ncct, "invalid section name\n");
986     return -1;
987   }
988   if(!strcmp(argv[0], "check") ||
989      !strcmp(argv[0], "noit") ||
990      !strcmp(argv[0], "filterset") ||
991      !strcmp(argv[0], "config")) {
992     nc_printf(ncct, "%s is reserved.\n", argv[0]);
993     return -1;
994   }
995   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
996   if(!strcmp(info->path, "/")) {
997     nc_printf(ncct, "manipulation of toplevel section disallowed\n");
998     return -1;
999   }
1000
1001   if(delete) {
1002     /* We cannot delete if we have checks */
1003     snprintf(xpath, sizeof(xpath), "/%s%s/%s//check", root_node_name,
1004              info->path, argv[0]);
1005     pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
1006     if(!pobj || pobj->type != XPATH_NODESET ||
1007        !xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
1008       err = "cannot delete section, has checks";
1009       goto bad;
1010     }
1011     if(pobj) xmlXPathFreeObject(pobj);
1012   }
1013
1014   snprintf(xpath, sizeof(xpath), "/%s%s/%s", root_node_name,
1015            info->path, argv[0]);
1016   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
1017   if(!pobj || pobj->type != XPATH_NODESET) {
1018     err = "internal error: cannot detect section";
1019     goto bad;
1020   }
1021   if(!delete && !xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
1022     if(xmlXPathNodeSetGetLength(pobj->nodesetval) == 1) {
1023       node = xmlXPathNodeSetItem(pobj->nodesetval, 0);
1024       if(info->path) free(info->path);
1025       info->path = strdup((char *)xmlGetNodePath(node) +
1026                           1 + strlen(root_node_name));
1027       goto cdout;
1028     }
1029     err = "cannot create section";
1030     goto bad;
1031   }
1032   if(delete && xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
1033     err = "no such section";
1034     goto bad;
1035   }
1036   if(delete) {
1037     node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
1038     xmlUnlinkNode(node);
1039     noit_conf_mark_changed();
1040     return 0;
1041   }
1042   if(pobj) xmlXPathFreeObject(pobj);
1043   pobj = NULL;
1044
1045   if(!strcmp(argv[0],"include")) {
1046     err = "include is a reserved section name";
1047     goto bad;
1048   }
1049   path = strcmp(info->path, "/") ? info->path : "";
1050   snprintf(xpath, sizeof(xpath), "/%s%s", root_node_name, path);
1051   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
1052   if(!pobj || pobj->type != XPATH_NODESET ||
1053      xmlXPathNodeSetGetLength(pobj->nodesetval) != 1) {
1054     err = "path invalid?";
1055     goto bad;
1056   }
1057   node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
1058   if((newnode = xmlNewChild(node, NULL, (xmlChar *)argv[0], NULL)) != NULL) {
1059     noit_conf_mark_changed();
1060     if(info->path) free(info->path);
1061     info->path = strdup((char *)xmlGetNodePath(newnode) + 1 +
1062                         strlen(root_node_name));
1063   }
1064   else {
1065     err = "failed to create section";
1066     goto bad;
1067   }
1068  cdout:
1069   if(pobj) xmlXPathFreeObject(pobj);
1070   return 0;
1071  bad:
1072   if(pobj) xmlXPathFreeObject(pobj);
1073   nc_printf(ncct, "%s\n", err);
1074   return -1;
1075 }
1076
1077 int
1078 noit_console_generic_show(noit_console_closure_t ncct,
1079                           int argc, char **argv,
1080                           noit_console_state_t *state, void *closure) {
1081   int i, cnt, titled = 0, cliplen = 0;
1082   const char *path = "", *basepath = NULL;
1083   char xpath[1024];
1084   noit_conf_t_userdata_t *info = NULL;
1085   xmlXPathObjectPtr pobj = NULL;
1086   xmlXPathContextPtr xpath_ctxt = NULL, current_ctxt;
1087   xmlDocPtr master_config = NULL;
1088   xmlNodePtr node = NULL;
1089
1090   noit_conf_xml_xpath(&master_config, &xpath_ctxt);
1091   if(argc > 1) {
1092     nc_printf(ncct, "too many arguments\n");
1093     return -1;
1094   }
1095
1096   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
1097   if(info) path = basepath = info->path;
1098   if(!info && argc == 0) {
1099     nc_printf(ncct, "argument required when not in configuration mode\n");
1100     return -1;
1101   }
1102
1103   if(argc == 1) path = argv[0];
1104   if(!basepath) basepath = path;
1105
1106   /* { / } is a special case */
1107   if(!strcmp(basepath, "/")) basepath = "";
1108   if(!strcmp(path, "/")) path = "";
1109
1110   if(!master_config) {
1111     nc_printf(ncct, "no config\n");
1112     return -1;
1113   }
1114
1115   /* { / } is the only path that will end with a /
1116    * in XPath { / / * } means something _entirely different than { / * }
1117    * Ever notice how it is hard to describe xpath in C comments?
1118    */
1119   /* We don't want to show the root node */
1120   cliplen = strlen(root_node_name) + 2; /* /name/ */
1121
1122   /* If we are in configuration mode
1123    * and we are without an argument or the argument is absolute,
1124    * clip the current path off */
1125   if(info && (argc == 0 || path[0] != '/')) cliplen += strlen(basepath);
1126   if(!path[0] || path[0] == '/') /* base only, or absolute path requested */
1127     snprintf(xpath, sizeof(xpath), "/%s%s/@*", root_node_name, path);
1128   else
1129     snprintf(xpath, sizeof(xpath), "/%s%s/%s/@*", root_node_name,
1130              basepath, path);
1131
1132   current_ctxt = xpath_ctxt;
1133   pobj = xmlXPathEval((xmlChar *)xpath, current_ctxt);
1134   if(!pobj || pobj->type != XPATH_NODESET) {
1135     nc_printf(ncct, "no such object\n");
1136     goto bad;
1137   }
1138   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
1139   titled = 0;
1140   for(i=0; i<cnt; i++) {
1141     node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
1142     if(node->children && node->children == xmlGetLastChild(node) &&
1143       xmlNodeIsText(node->children)) {
1144       if(!titled++) nc_printf(ncct, "== Section Settings ==\n");
1145       nc_printf(ncct, "%s: %s\n", xmlGetNodePath(node) + cliplen,
1146                 xmlXPathCastNodeToString(node->children));
1147     }
1148   }
1149   xmlXPathFreeObject(pobj);
1150
1151   /* _shorten string_ turning last { / @ * } to { / * } */
1152   if(!path[0] || path[0] == '/') /* base only, or absolute path requested */
1153     snprintf(xpath, sizeof(xpath), "/%s%s/*", root_node_name, path);
1154   else
1155     snprintf(xpath, sizeof(xpath), "/%s%s/%s/*",
1156              root_node_name, basepath, path);
1157   pobj = xmlXPathEval((xmlChar *)xpath, current_ctxt);
1158   if(!pobj || pobj->type != XPATH_NODESET) {
1159     nc_printf(ncct, "no such object\n");
1160     goto bad;
1161   }
1162   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
1163   titled = 0;
1164   for(i=0; i<cnt; i++) {
1165     node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
1166     if(!(node->children && node->children == xmlGetLastChild(node) &&
1167          xmlNodeIsText(node->children))) {
1168       if(!titled++) nc_printf(ncct, "== Subsections ==\n");
1169       nc_printf(ncct, "%s\n", xmlGetNodePath(node) + cliplen);
1170     }
1171   }
1172   xmlXPathFreeObject(pobj);
1173   return 0;
1174  bad:
1175   if(pobj) xmlXPathFreeObject(pobj);
1176   return -1;
1177 }
1178 int
1179 noit_console_config_cd(noit_console_closure_t ncct,
1180                        int argc, char **argv,
1181                        noit_console_state_t *state, void *closure) {
1182   const char *err = "internal error";
1183   char *path, xpath[1024];
1184   noit_conf_t_userdata_t *info;
1185   xmlXPathObjectPtr pobj = NULL;
1186   xmlXPathContextPtr xpath_ctxt = NULL, current_ctxt;
1187   xmlNodePtr node = NULL;
1188   char *dest;
1189
1190   noit_conf_xml_xpath(NULL, &xpath_ctxt);
1191   if(argc != 1 && !closure) {
1192     nc_printf(ncct, "requires one argument\n");
1193     return -1;
1194   }
1195   dest = argc ? argv[0] : (char *)closure;
1196   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
1197   if(dest[0] == '/')
1198     snprintf(xpath, sizeof(xpath), "/%s%s", root_node_name, dest);
1199   else {
1200     snprintf(xpath, sizeof(xpath), "/%s%s/%s", root_node_name,
1201              info->path, dest);
1202   }
1203   if(xpath[strlen(xpath)-1] == '/') xpath[strlen(xpath)-1] = '\0';
1204
1205   current_ctxt = xpath_ctxt;
1206   pobj = xmlXPathEval((xmlChar *)xpath, current_ctxt);
1207   if(!pobj || pobj->type != XPATH_NODESET ||
1208      xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
1209     err = "no such section";
1210     goto bad;
1211   }
1212   if(xmlXPathNodeSetGetLength(pobj->nodesetval) > 1) {
1213     err = "ambiguous section";
1214     goto bad;
1215   }
1216
1217   node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
1218   if(!strcmp((char *)node->name, "check") ||
1219      !strcmp((char *)node->name, "noit") ||
1220      !strcmp((char *)node->name, "filterset") ||
1221      !strcmp((char *)node->name, "config")) {
1222     err = "reserved word";
1223     goto bad;
1224   }
1225   path = (char *)xmlGetNodePath(node);
1226   if(strlen(path) < strlen(root_node_name) + 1 ||
1227      strncmp(path + 1, root_node_name, strlen(root_node_name)) ||
1228      (path[strlen(root_node_name) + 1] != '/' &&
1229       path[strlen(root_node_name) + 1] != '\0')) {
1230     err = "new path outside out tree";
1231     goto bad;
1232   }
1233   free(info->path);
1234   if(!strcmp(path + 1, root_node_name))
1235     info->path = strdup("/");
1236   else
1237     info->path = strdup((char *)xmlGetNodePath(node) + 1 +
1238                         strlen(root_node_name));
1239   if(pobj) xmlXPathFreeObject(pobj);
1240   if(closure) noit_console_state_pop(ncct, argc, argv, NULL, NULL);
1241   return 0;
1242  bad:
1243   if(pobj) xmlXPathFreeObject(pobj);
1244   nc_printf(ncct, "%s [%s]\n", err, xpath);
1245   return -1;
1246 }
1247
1248 char *
1249 conf_t_prompt(EditLine *el) {
1250   noit_console_closure_t ncct;
1251   noit_conf_t_userdata_t *info;
1252   static char *tl = "noit(conf)# ";
1253   static char *pfmt = "noit(conf:%s%s)# ";
1254   int path_len, max_len;
1255
1256   el_get(el, EL_USERDATA, (void *)&ncct);
1257   if(!ncct) return tl;
1258   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
1259   if(!info) return tl;
1260
1261   path_len = strlen(info->path);
1262   max_len = sizeof(info->prompt) - (strlen(pfmt) - 4 /* %s%s */) - 1 /* \0 */;
1263   if(path_len > max_len)
1264     snprintf(info->prompt, sizeof(info->prompt),
1265              pfmt, "...", info->path + path_len - max_len + 3 /* ... */);
1266   else
1267     snprintf(info->prompt, sizeof(info->prompt), pfmt, "", info->path);
1268   return info->prompt;
1269 }
1270
1271 #define NEW_STATE(a) (a) = noit_console_state_alloc()
1272 #define ADD_CMD(a,cmd,func,ac,ss,c) \
1273   noit_console_state_add_cmd((a), \
1274     NCSCMD(cmd, func, ac, ss, c))
1275 #define DELEGATE_CMD(a,cmd,ac,ss) \
1276   noit_console_state_add_cmd((a), \
1277     NCSCMD(cmd, noit_console_state_delegate, ac, ss, NULL))
1278
1279 void noit_console_conf_init() {
1280   noit_console_state_t *tl, *_conf_state, *_conf_t_state,
1281                        *_write_state, *_unset_state;
1282
1283   tl = noit_console_state_initial();
1284
1285   /* write <terimal|memory|file> */
1286   NEW_STATE(_write_state);
1287   ADD_CMD(_write_state, "terminal", noit_conf_write_terminal, NULL, NULL, NULL);
1288   ADD_CMD(_write_state, "file", noit_conf_write_file_console, NULL, NULL, NULL);
1289   /* write memory?  It's to a file, but I like router syntax */
1290   ADD_CMD(_write_state, "memory", noit_conf_write_file_console, NULL, NULL, NULL);
1291
1292   NEW_STATE(_unset_state);
1293   ADD_CMD(_unset_state, "section",
1294           noit_console_config_section, NULL, NULL, (void *)1);
1295
1296   NEW_STATE(_conf_t_state);
1297   _conf_t_state->console_prompt_function = conf_t_prompt;
1298   noit_console_state_add_cmd(_conf_t_state, &console_command_exit);
1299
1300   ADD_CMD(_conf_t_state, "ls", noit_console_generic_show, NULL, NULL, NULL);
1301   ADD_CMD(_conf_t_state, "cd", noit_console_config_cd, NULL, NULL, NULL);
1302   ADD_CMD(_conf_t_state, "section",
1303           noit_console_config_section, NULL, NULL, (void *)0);
1304
1305   DELEGATE_CMD(_conf_t_state, "write",
1306                noit_console_opt_delegate, _write_state);
1307   DELEGATE_CMD(_conf_t_state, "no", noit_console_opt_delegate, _unset_state);
1308
1309   NEW_STATE(_conf_state);
1310   ADD_CMD(_conf_state, "terminal",
1311           noit_console_state_conf_terminal, NULL, _conf_t_state, NULL);
1312
1313   ADD_CMD(tl, "configure",
1314           noit_console_state_delegate, noit_console_opt_delegate,
1315           _conf_state, NULL);
1316   ADD_CMD(tl, "write",
1317           noit_console_state_delegate, noit_console_opt_delegate,
1318           _write_state, NULL);
1319 }
1320
Note: See TracBrowser for help on using the browser.