root/src/noit_conf.c

Revision 0fb8722b06c63b10dd6cff3a04967a8f4b9d3d36, 70.8 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 1 day ago)

remove debugging

  • 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 <dirent.h>
39 #include <errno.h>
40 #include <assert.h>
41 #include <libxml/parser.h>
42 #include <libxml/tree.h>
43 #include <libxml/xpath.h>
44 #include <zlib.h>
45
46 #include "noit_conf.h"
47 #include "noit_check.h"
48 #include "noit_console.h"
49 #include "noit_version.h"
50 #include "noit_xml.h"
51 #include "utils/noit_hash.h"
52 #include "utils/noit_log.h"
53 #include "utils/noit_b64.h"
54 #include "utils/noit_watchdog.h"
55
56 const char *_noit_branch = NOIT_BRANCH;
57 const char *_noit_version = NOIT_VERSION;
58
59 /* tmp hash impl, replace this with something nice */
60 static noit_log_stream_t xml_debug = NULL;
61 #define XML2LOG(log) do { \
62   xmlSetGenericErrorFunc(log, noit_conf_xml_error_func); \
63   xmlSetStructuredErrorFunc(log, noit_conf_xml_error_ext_func); \
64 } while(0)
65 #define XML2CONSOLE(ncct) do { \
66   xmlSetGenericErrorFunc(ncct, noit_conf_xml_console_error_func); \
67   xmlSetStructuredErrorFunc(ncct, noit_conf_xml_console_error_ext_func); \
68 } while(0)
69 static xmlDocPtr master_config = NULL;
70 static int config_include_cnt = -1;
71 static int backingstore_include_cnt = -1;
72
73 struct include_node_t{
74   xmlNodePtr insertion_point;
75   xmlNodePtr old_children;
76   xmlDocPtr doc;
77   xmlNodePtr root;
78   int snippet;
79   int ro;
80   char path[255];
81   int child_count;
82   struct include_node_t *children;
83 };
84
85 typedef struct include_node_t include_node_t;
86
87 static include_node_t *config_include_nodes = NULL,
88                       *backingstore_include_nodes = NULL;
89
90 typedef struct noit_xml_userdata {
91   char       *name;
92   char       *path;
93   u_int64_t   dirty_time;
94   struct noit_xml_userdata *freelist;
95 } noit_xml_userdata_t;
96
97 static noit_xml_userdata_t *backingstore_freelist = NULL;
98 static u_int64_t last_config_flush = 0;
99
100 #define is_stopnode_name(n) ((n) && \
101     (!strcmp((char *)(n), "check") || \
102      !strcmp((char *)(n), "config") || \
103      !strcmp((char *)(n), "filterset")))
104 #define is_stopnode(node) ((node) && is_stopnode_name((node)->name))
105
106 static char *root_node_name = NULL;
107 static char master_config_file[PATH_MAX] = "";
108 static xmlXPathContextPtr xpath_ctxt = NULL;
109
110 /* coalesced writing allows internals to change the XML structure and mark
111  * the tree dirty, but only write the config out once per second.
112  */
113 static u_int32_t __coalesce_write = 0;
114
115 /* This is used to notice config changes and journal the config out
116  * using a user-specified function.  It supports allowing multiple config
117  * changed to coalesce so you don't write out 1000 changes in a few seconds.
118  */
119 static u_int32_t __config_gen = 0;
120 static u_int32_t __config_coalesce = 0;
121 static u_int32_t __config_coalesce_time = 0;
122 static u_int64_t max_gen_count = 0;
123 void noit_conf_coalesce_changes(u_int32_t seconds) {
124   __config_coalesce_time = seconds;
125 }
126 void noit_conf_request_write() {
127   __coalesce_write = 1;
128 }
129 void noit_conf_mark_changed() {
130   /* increment the change counter -- in case anyone cares */
131   __config_gen++;
132   /* reset the coalesce counter.  It is decremented each second and
133    * the journal function fires on a transition from 1 => 0
134    */
135   __config_coalesce = __config_coalesce_time;
136 }
137 struct recurrent_journaler {
138   int (*journal_config)(void *);
139   void *jc_closure;
140 };
141
142 void noit_conf_write_section(noit_conf_section_t node, int fd) {
143   xmlOutputBufferPtr out;
144   xmlCharEncodingHandlerPtr enc;
145
146   enc = xmlGetCharEncodingHandler(XML_CHAR_ENCODING_UTF8);
147   out = xmlOutputBufferCreateFd(fd, enc);
148   xmlNodeDumpOutput(out, master_config, node, 2, 0, "utf8");
149   xmlOutputBufferClose(out);
150   write(2, "\n", 1);
151   xmlFree(enc);
152 }
153 void
154 write_out_include_files(include_node_t *include_nodes, int include_node_cnt) {
155   int i;
156   for(i=0; i<include_node_cnt; i++) {
157     xmlOutputBufferPtr out;
158     xmlCharEncodingHandlerPtr enc;
159     mode_t mode = 0640;
160     char filename[500];
161     int len, fd;
162     struct stat st;
163     uid_t uid = 0;
164     gid_t gid = 0;
165
166     if(include_nodes[i].ro) {
167       write_out_include_files(include_nodes[i].children, include_nodes[i].child_count);
168       continue;
169     }
170     if(stat(include_nodes[i].path, &st) == 0) {
171       mode = st.st_mode;
172       uid = st.st_uid;
173       gid = st.st_gid;
174     }
175
176     sprintf(filename, "%s.tmp", include_nodes[i].path);
177     fd = open(filename, O_CREAT|O_TRUNC|O_WRONLY, mode);
178     fchown(fd, uid, gid);
179
180     enc = xmlGetCharEncodingHandler(XML_CHAR_ENCODING_UTF8);
181     out = xmlOutputBufferCreateFd(fd, enc);
182     len = xmlSaveFormatFileTo(out, include_nodes[i].doc, "utf8", 1);
183     if (len < 0) {
184       noitL(noit_error, "couldn't write out %s\n", include_nodes[i].path);
185       close(fd);
186       continue;
187     }
188     close(fd);
189     write_out_include_files(include_nodes[i].children, include_nodes[i].child_count);
190     if(rename(filename, include_nodes[i].path) != 0) {
191       noitL(noit_error, "Failed to replace file %s: %s\n", include_nodes[i].path, strerror(errno));
192     }
193   }
194 }
195
196 static void
197 noit_xml_userdata_free(noit_xml_userdata_t *n) {
198   if(n->name) free(n->name);
199   if(n->path) free(n->path);
200 }
201
202 void
203 noit_conf_set_namespace(const char *ns) {
204   xmlNsPtr nsptr;
205   xmlNodePtr root;
206   root = xmlDocGetRootElement(master_config);
207   nsptr = xmlSearchNs(master_config, root, (xmlChar *)ns);
208   if(!nsptr) {
209     char url[128];
210     snprintf(url, sizeof(url), "noit://module/%s", ns);
211     xmlNewNs(root, (xmlChar *)url, (xmlChar *)ns);
212   }
213 }
214
215 static int
216 noit_conf_watch_config_and_journal(eventer_t e, int mask, void *closure,
217                                    struct timeval *now) {
218   struct recurrent_journaler *rj = closure;
219   eventer_t newe;
220
221   if(__config_coalesce == 1)
222     rj->journal_config(rj->jc_closure);
223   if(__config_coalesce > 0)
224     __config_coalesce--;
225
226   if(__coalesce_write) {
227     noit_conf_write_file(NULL);
228     __coalesce_write = 0;
229   }
230
231   /* Schedule the same event to fire a second form now */
232   newe = eventer_alloc();
233   gettimeofday(&newe->whence, NULL);
234   newe->whence.tv_sec += 1;
235   newe->mask = EVENTER_TIMER;
236   newe->callback = noit_conf_watch_config_and_journal;
237   newe->closure = closure;
238   eventer_add(newe);
239   return 0;
240 }
241
242 void
243 noit_conf_watch_and_journal_watchdog(int (*f)(void *), void *c) {
244   static int callbacknamed = 0;
245   struct recurrent_journaler *rj;
246   struct timeval __now;
247
248   if(!callbacknamed) {
249     callbacknamed = 1;
250     eventer_name_callback("noit_conf_watch_config_and_journal",
251                           noit_conf_watch_config_and_journal);
252   }
253   rj = calloc(1, sizeof(*rj));
254   rj->journal_config = f;
255   rj->jc_closure = c;
256   gettimeofday(&__now, NULL);
257   noit_conf_watch_config_and_journal(NULL, EVENTER_TIMER, rj, &__now);
258 }
259
260 static noit_hash_table _compiled_fallback = NOIT_HASH_EMPTY;
261 static struct {
262   const char *key;
263   const char *val;
264 } config_info[] = {
265   /*
266    * These are compile-time fallbacks to be used in the event
267    * that the current running config does not have values for
268    * these config paths.
269    *
270    * PLEASE: keep them alphabetically sorted.
271    */
272   { "/%s/eventer/@implementation", DEFAULT_EVENTER },
273   { "/%s/modules/@directory", MODULES_DIR },
274
275   { NULL, NULL }
276 };
277
278 void noit_conf_xml_console_error_func(void *ctx, const char *format, ...) {
279   noit_console_closure_t ncct = ctx;
280   va_list arg;
281   if(!ncct) return;
282   va_start(arg, format);
283   nc_vprintf(ncct, format, arg);
284   va_end(arg);
285 }
286
287 void noit_conf_xml_console_error_ext_func(void *ctx, xmlErrorPtr err) {
288   noit_console_closure_t ncct = ctx;
289   if(!ctx) return;
290   if(err->file)
291     nc_printf(ncct, "XML error [%d/%d] in %s on line %d %s\n",
292               err->domain, err->code, err->file, err->line, err->message);
293   else
294     nc_printf(ncct, "XML error [%d/%d] %s\n",
295               err->domain, err->code, err->message);
296 }
297
298 void noit_conf_xml_error_func(void *ctx, const char *format, ...) {
299   struct timeval __now;
300   noit_log_stream_t ls = ctx;
301   va_list arg;
302   if(!ls) return;
303   va_start(arg, format);
304   gettimeofday(&__now,  NULL);
305   noit_vlog(ls, &__now, __FILE__, __LINE__, format, arg);
306   va_end(arg);
307 }
308 void noit_conf_xml_error_ext_func(void *ctx, xmlErrorPtr err) {
309   struct timeval __now;
310   noit_log_stream_t ls = ctx;
311   if(!ls) return;
312   gettimeofday(&__now,  NULL);
313   if(err->file)
314     noit_log(ls, &__now, err->file, err->line,
315              "XML error [%d/%d] in %s on line %d %s\n",
316              err->domain, err->code, err->file, err->line, err->message);
317   else
318     noit_log(ls, &__now, err->file, err->line,
319              "XML error [%d/%d] %s\n",
320              err->domain, err->code, err->message);
321 }
322
323
324 void
325 noit_conf_poke(const char *toplevel, const char *key, const char *val) {
326   char keystr[256];
327   snprintf(keystr, sizeof(keystr), key, toplevel);
328   noit_hash_store(&_compiled_fallback,
329                   strdup(keystr), strlen(keystr),
330                   (void *)strdup(val));
331 }
332
333 DECLARE_CHECKER(name)
334 void noit_conf_init(const char *toplevel) {
335   int i;
336
337   xml_debug = noit_log_stream_find("debug/xml");
338
339   COMPILE_CHECKER(name, "^[-_\\.:/a-zA-Z0-9]+$");
340   XML2LOG(noit_error);
341   for(i = 0; config_info[i].key != NULL; i++)
342     noit_conf_poke(toplevel, config_info[i].key, config_info[i].val);
343   xmlKeepBlanksDefault(0);
344   xmlInitParser();
345   xmlXPathInit();
346 }
347
348 void
349 noit_conf_magic_separate_includes(include_node_t **root_include_nodes, int *cnt) {
350   include_node_t *include_nodes = *root_include_nodes;
351   assert(*cnt != -1);
352   if(include_nodes) {
353     int i;
354     for(i=0; i<*cnt; i++) {
355       noit_conf_magic_separate_includes(&(include_nodes[i].children), &(include_nodes[i].child_count));
356       if(include_nodes[i].doc) {
357         xmlNodePtr n;
358         for(n=include_nodes[i].insertion_point->children;
359             n; n = n->next)
360           n->parent = include_nodes[i].root;
361         include_nodes[i].insertion_point->children =
362           include_nodes[i].old_children;
363         xmlFreeDoc(include_nodes[i].doc);
364       }
365     }
366     free(include_nodes);
367   }
368   *root_include_nodes = NULL;
369   *cnt = -1;
370 }
371
372 void
373 noit_conf_magic_separate() {
374   noit_conf_magic_separate_includes(&config_include_nodes, &config_include_cnt);
375   assert(config_include_nodes == NULL);
376   if(backingstore_include_nodes) {
377     int i;
378     for(i=0; i<backingstore_include_cnt; i++) {
379       if(backingstore_include_nodes[i].doc) {
380         xmlNodePtr n;
381         for(n=backingstore_include_nodes[i].insertion_point->children;
382             n; n = n->next) {
383           n->parent = backingstore_include_nodes[i].root;
384           n->parent->last = n;
385         }
386         backingstore_include_nodes[i].insertion_point->children =
387           backingstore_include_nodes[i].old_children;
388         for(n=backingstore_include_nodes[i].insertion_point->children;
389             n; n = n->next) {
390           n->parent->last = n; /* sets it to the last child */
391         }
392         xmlFreeDoc(backingstore_include_nodes[i].doc);
393       }
394     }
395     free(backingstore_include_nodes);
396   }
397   backingstore_include_nodes = NULL;
398   backingstore_include_cnt = -1;
399 }
400 void
401 noit_conf_kansas_city_shuffle_redo(include_node_t *include_nodes, int include_node_cnt) {
402   if(include_nodes) {
403     int i;
404     for(i=0; i<include_node_cnt; i++) {
405       noit_conf_kansas_city_shuffle_redo(include_nodes[i].children, include_nodes[i].child_count);
406       if(include_nodes[i].doc) {
407         xmlNodePtr n;
408
409         if (!include_nodes[i].snippet)
410           include_nodes[i].insertion_point->children =
411             include_nodes[i].root->children;
412         else
413           include_nodes[i].insertion_point->children =
414             include_nodes[i].root;
415
416         for(n=include_nodes[i].insertion_point->children;
417             n; n = n->next)
418           n->parent = include_nodes[i].insertion_point;
419       }
420     }
421   }
422 }
423 void
424 noit_conf_kansas_city_shuffle_undo(include_node_t *include_nodes, int include_node_cnt) {
425   if(include_nodes) {
426     int i;
427     for(i=0; i<include_node_cnt; i++) {
428       noit_conf_kansas_city_shuffle_undo(include_nodes[i].children, include_nodes[i].child_count);
429       if(include_nodes[i].doc) {
430         xmlNodePtr n;
431         for(n=include_nodes[i].insertion_point->children;
432             n; n = n->next)
433           n->parent = include_nodes[i].root;
434         include_nodes[i].insertion_point->children =
435           include_nodes[i].old_children;
436       }
437     }
438   }
439 }
440 static u_int64_t
441 usec_now() {
442   u_int64_t usec;
443   struct timeval tv;
444   gettimeofday(&tv, NULL);
445   usec = tv.tv_sec * 1000000UL;
446   usec += tv.tv_usec;
447   return usec;
448 }
449
450 static void
451 remove_emancipated_child_node(xmlNodePtr oldp, xmlNodePtr node) {
452   /* node was once a child of oldp... it's still in it's children list
453    * but node's parent isn't this child.
454    */
455   assert(node->parent != oldp);
456   if(oldp->children == NULL) return;
457   if(oldp->children == node) {
458     oldp->children = node->next;
459     if (node->next) node->next->prev = node->prev;
460   }
461   else {
462     xmlNodePtr prev = oldp->children;
463     for(prev = oldp->children; prev->next && prev->next != node; prev = prev->next);
464     if(prev) prev->next = node->next;
465     if(node->next) node->next->prev = prev;
466   }
467 }
468 void
469 noit_conf_include_remove(noit_conf_section_t vnode) {
470   int i;
471   xmlNodePtr node = vnode;
472   for(i=0;i<config_include_cnt;i++) {
473     if(node->parent == config_include_nodes[i].insertion_point) {
474       remove_emancipated_child_node(config_include_nodes[i].root, node);
475     }
476   }
477 }
478 void
479 noit_conf_backingstore_remove(noit_conf_section_t vnode) {
480   int i;
481   xmlNodePtr node = vnode;
482   noit_xml_userdata_t *subctx = node->_private;
483   for(i=0;i<backingstore_include_cnt;i++) {
484     if(node->parent == backingstore_include_nodes[i].insertion_point) {
485       remove_emancipated_child_node(backingstore_include_nodes[i].root, node);
486     }
487   }
488   if(subctx) {
489     noitL(noit_debug, "marking %s for removal\n", subctx->path);
490     if(!backingstore_freelist) backingstore_freelist = subctx;
491     else {
492       noit_xml_userdata_t *fl = backingstore_freelist;
493       while(fl->freelist) fl = fl->freelist;
494       fl->freelist = subctx;
495     }
496     node->_private = NULL;
497   }
498   /* If we're deleted, we'll mark the parent as dirty */
499   if(node->parent) noit_conf_backingstore_dirty(node->parent);
500 }
501 void
502 noit_conf_backingstore_dirty(noit_conf_section_t vnode) {
503   xmlNodePtr node = vnode;
504   noit_xml_userdata_t *subctx = node->_private;
505   if(subctx) {
506     noitL(noit_debug, "backingstore[%s] marked dirty\n", subctx->path);
507     subctx->dirty_time = usec_now();
508     return;
509   }
510   if(node->parent) noit_conf_backingstore_dirty(node->parent);
511 }
512 int
513 noit_conf_backingstore_write(noit_xml_userdata_t *ctx, noit_boolean skip,
514                              xmlAttrPtr attrs, xmlNodePtr node) {
515   int failure = 0;
516   char newpath[PATH_MAX];
517   xmlNodePtr n;
518   snprintf(newpath, sizeof(newpath), "%s/.attrs", ctx->path);
519   if(attrs) {
520     xmlDocPtr tmpdoc;
521     xmlNodePtr tmpnode;
522     noitL(noit_debug, " **> %s\n", newpath);
523     tmpdoc = xmlNewDoc((xmlChar *)"1.0");
524     tmpnode = xmlNewNode(NULL, ctx->name ? (xmlChar *)ctx->name : (xmlChar *)"stub");
525     xmlDocSetRootElement(tmpdoc, tmpnode);
526     tmpnode->properties = attrs;
527     failure = noit_xmlSaveToFile(tmpdoc, newpath);
528     tmpnode->properties = NULL;
529     xmlFreeDoc(tmpdoc);
530     if(failure) return -1;
531   }
532   else if(!skip) {
533     unlink(newpath);
534   }
535   for(n = node; n; n = n->next) {
536     int leaf;
537     noit_xml_userdata_t *subctx;
538     subctx = n->_private;
539     leaf = is_stopnode(n);
540     if(!subctx) { /* This has never been written out */
541       subctx = calloc(1, sizeof(*subctx));
542       subctx->name = strdup((char *)n->name);
543       snprintf(newpath, sizeof(newpath), "%s/%s#%llu", ctx->path, n->name,
544                (unsigned long long)++max_gen_count);
545       if(leaf) strlcat(newpath, ".xml", sizeof(newpath));
546       subctx->path = strdup(newpath);
547       subctx->dirty_time = usec_now();
548       n->_private = subctx;
549       noitL(noit_debug, " !!> %s\n", subctx->path);
550     }
551     if(leaf) {
552       xmlDocPtr tmpdoc;
553       xmlNodePtr tmpnode;
554       if(subctx->dirty_time > last_config_flush) {
555         xmlNsPtr *parent_nslist, iter_ns;
556         xmlNodePtr root;
557         root = xmlDocGetRootElement(master_config);
558         parent_nslist = xmlGetNsList(master_config, root);
559
560         tmpdoc = xmlNewDoc((xmlChar *)"1.0");
561         tmpnode = xmlNewNode(NULL, n->name);
562         xmlDocSetRootElement(tmpdoc, tmpnode);
563         if(parent_nslist) {
564           for(iter_ns = *parent_nslist; iter_ns; iter_ns = iter_ns->next)
565             xmlNewNs(tmpnode, iter_ns->href, iter_ns->prefix);
566           xmlFree(parent_nslist);
567         }
568         tmpnode->properties = n->properties;
569         tmpnode->children = n->children;
570         failure = noit_xmlSaveToFile(tmpdoc, subctx->path);
571         tmpnode->properties = NULL;
572         tmpnode->children = NULL;
573         xmlFreeDoc(tmpdoc);
574         noitL(noit_debug, " ==> %s\n", subctx->path);
575         if(failure) return -1;
576       }
577     }
578     else {
579       noit_boolean skip_attrs;
580       skip_attrs = leaf || (subctx->dirty_time <= last_config_flush);
581       noitL(noit_debug, " --> %s\n", subctx->path);
582       if(noit_conf_backingstore_write(subctx, skip_attrs, skip_attrs ? NULL : n->properties, n->children))
583         return -1;
584     }
585   }
586   return 0;
587 }
588 void
589 noit_conf_shatter_write(xmlDocPtr doc) {
590   if(backingstore_freelist) {
591     noit_xml_userdata_t *fl, *last;
592     for(fl = backingstore_freelist; fl; ) {
593       last = fl;
594       fl = fl->freelist;
595       /* If it is a file, we'll unlink it, otherwise,
596        * we need to delete the attributes and the directory.
597        */
598       if(unlink(last->path)) {
599         char attrpath[PATH_MAX];
600         snprintf(attrpath, sizeof(attrpath), "%s/.attrs", last->path);
601         unlink(attrpath);
602         if(rmdir(last->path) && errno != ENOENT) {
603           /* This shouldn't happen, but if it does we risk
604            * leaving a mess. Don't do that.
605            */
606           noitL(noit_error, "backingstore mess %s: %s\n",
607                 last->path, strerror(errno));
608         }
609       }
610       noit_xml_userdata_free(last);
611     }
612     backingstore_freelist = NULL;
613   }
614   if(backingstore_include_nodes) {
615     int i;
616     for(i=0; i<backingstore_include_cnt; i++) {
617       if(backingstore_include_nodes[i].doc) {
618         xmlNodePtr n;
619         noit_xml_userdata_t *what = backingstore_include_nodes[i].doc->_private;
620
621         for(n=backingstore_include_nodes[i].insertion_point->children;
622             n; n = n->next) {
623           n->parent = backingstore_include_nodes[i].root;
624           n->parent->last = n;
625         }
626         backingstore_include_nodes[i].root->children =
627           backingstore_include_nodes[i].insertion_point->children;
628         backingstore_include_nodes[i].insertion_point->children =
629           backingstore_include_nodes[i].old_children;
630         for(n=backingstore_include_nodes[i].insertion_point->children;
631             n; n = n->next) {
632           n->parent->last = n; /* sets it to the last child */
633         }
634         noit_conf_backingstore_write(what, noit_false, NULL, backingstore_include_nodes[i].root->children);
635       }
636     }
637     last_config_flush = usec_now();
638   }
639 }
640 void
641 noit_conf_shatter_postwrite(xmlDocPtr doc) {
642   if(backingstore_include_nodes) {
643     int i;
644     for(i=0; i<backingstore_include_cnt; i++) {
645       if(backingstore_include_nodes[i].doc) {
646         xmlNodePtr n;
647         backingstore_include_nodes[i].insertion_point->children =
648           backingstore_include_nodes[i].root->children;
649         for(n=backingstore_include_nodes[i].insertion_point->children;
650             n; n = n->next) {
651           n->parent = backingstore_include_nodes[i].insertion_point;
652           n->parent->last = n;
653         }
654       }
655     }
656   }
657 }
658
659 int
660 noit_conf_read_into_node(xmlNodePtr node, const char *path) {
661   DIR *dirroot;
662   struct dirent *de, *entry;
663   char filepath[PATH_MAX];
664   xmlDocPtr doc;
665   xmlNodePtr root = NULL;
666   struct stat sb;
667   int size, rv;
668
669   noitL(noit_debug, "read backing store: %s\n", path);
670   snprintf(filepath, sizeof(filepath), "%s/.attrs", path);
671   while((rv = stat(filepath, &sb)) < 0 && errno == EINTR);
672   if(rv == 0) {
673     doc = xmlReadFile(filepath, "utf8", XML_PARSE_NOENT);
674     if(doc) root = xmlDocGetRootElement(doc);
675     if(doc && root) {
676       node->properties = xmlCopyPropList(node, root->properties);
677       xmlFreeDoc(doc);
678       doc = NULL;
679     }
680   }
681 #ifdef _PC_NAME_MAX
682   size = pathconf(path, _PC_NAME_MAX);
683 #endif
684   size = MAX(size, PATH_MAX + 128);
685   de = alloca(size);
686   dirroot = opendir(path);
687   if(!dirroot) return -1;
688   while(portable_readdir_r(dirroot, de, &entry) == 0 && entry != NULL) {
689     noit_xml_userdata_t *udata;
690     char name[PATH_MAX];
691     char *sep;
692     xmlNodePtr child;
693     u_int64_t gen;
694
695     noit_watchdog_child_heartbeat();
696
697     sep = strchr(entry->d_name, '#');
698     if(!sep) continue;
699     snprintf(filepath, sizeof(filepath), "%s/%s", path, entry->d_name);
700     while((rv = stat(filepath, &sb)) < 0 && errno == EINTR);
701     if(rv == 0) {
702       strlcpy(name, entry->d_name, sizeof(name));
703       name[sep - entry->d_name] = '\0';
704       gen = strtoull(sep+1, NULL, 10);
705       if(gen > max_gen_count) max_gen_count = gen;
706
707       if(S_ISDIR(sb.st_mode)) {
708         noitL(noit_debug, "<DIR< %s\n", entry->d_name);
709         child = xmlNewNode(NULL, (xmlChar *)name);
710         noit_conf_read_into_node(child, filepath);
711         udata = calloc(1, sizeof(*udata));
712         udata->name = strdup(name);
713         udata->path = strdup(filepath);
714         child->_private = udata;
715         xmlAddChild(node, child);
716       }
717       else if(S_ISREG(sb.st_mode)) {
718         xmlDocPtr cdoc;
719         xmlNodePtr cnode = NULL;
720         noitL(noit_debug, "<FILE< %s\n", entry->d_name);
721         cdoc = xmlParseFile(filepath);
722         if(cdoc) {
723           cnode = xmlDocGetRootElement(cdoc);
724           xmlDocSetRootElement(cdoc, xmlNewNode(NULL, (xmlChar *)"dummy"));
725           if(cnode) {
726             udata = calloc(1, sizeof(*udata));
727             udata->name = strdup(name);
728             udata->path = strdup(filepath);
729             cnode->_private = udata;
730             xmlAddChild(node, cnode);
731           }
732           xmlFreeDoc(cdoc);
733         }
734       }
735     }
736   }
737   closedir(dirroot);
738   return 0;
739 }
740
741 xmlDocPtr
742 noit_conf_read_backing_store(const char *path) {
743   xmlDocPtr doc;
744   xmlNodePtr root;
745   noit_xml_userdata_t *what;
746
747   doc = xmlNewDoc((xmlChar *)"1.0");
748   what = calloc(1, sizeof(*what));
749   what->path = strdup(path);
750   doc->_private = what;
751   root = xmlNewNode(NULL, (xmlChar *)"stub");
752   xmlDocSetRootElement(doc, root);
753   noit_conf_read_into_node(root, path);
754   return doc;
755 }
756 int
757 noit_conf_magic_mix(const char *parentfile, xmlDocPtr doc, include_node_t* inc_node) {
758   xmlXPathContextPtr mix_ctxt = NULL;
759   xmlXPathObjectPtr pobj = NULL;
760   xmlNodePtr node;
761   int i, cnt, rv = 0, master = 0;
762   int *include_cnt;
763   include_node_t* include_nodes;
764
765   if (inc_node) {
766     include_cnt = &(inc_node->child_count);
767     include_nodes = inc_node->children;
768   }
769   else {
770     include_cnt = &(config_include_cnt);
771     include_nodes = config_include_nodes;
772     master = 1;
773   }
774
775   assert(*include_cnt == -1);
776
777   if (master) {
778     assert(backingstore_include_cnt == -1);
779     backingstore_include_cnt = 0;
780   }
781   mix_ctxt = xmlXPathNewContext(doc);
782   if (master)
783     pobj = xmlXPathEval((xmlChar *)"//*[@backingstore]", mix_ctxt);
784   else
785     pobj = NULL;
786
787   if(!pobj) goto includes;
788   if(pobj->type != XPATH_NODESET) goto includes;
789   if(xmlXPathNodeSetIsEmpty(pobj->nodesetval)) goto includes;
790
791   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
792   if(cnt > 0)
793     backingstore_include_nodes = calloc(cnt, sizeof(*backingstore_include_nodes));
794   for(i=0; i<cnt; i++) {
795     char *path, *infile;
796     node = xmlXPathNodeSetItem(pobj->nodesetval, i);
797     path = (char *)xmlGetProp(node, (xmlChar *)"backingstore");
798     if(!path) continue;
799     if(*path == '/') infile = strdup(path);
800     else {
801       char *cp;
802       infile = malloc(PATH_MAX);
803       strlcpy(infile, parentfile, PATH_MAX);
804       for(cp = infile + strlen(infile) - 1; cp > infile; cp--) {
805         if(*cp == '/') { *cp = '\0'; break; }
806         else *cp = '\0';
807       }
808       strlcat(infile, "/", PATH_MAX);
809       strlcat(infile, path, PATH_MAX);
810     }
811     xmlFree(path);
812     backingstore_include_nodes[i].doc = noit_conf_read_backing_store(infile);
813     if(backingstore_include_nodes[i].doc) {
814       xmlNodePtr n, lchild;
815       backingstore_include_nodes[i].insertion_point = node;
816       backingstore_include_nodes[i].root = xmlDocGetRootElement(backingstore_include_nodes[i].doc);
817       /* for backing store, they are permanently reattached under the backing store.
818        * so for any children, we need to glue them into the new parent document.
819        */
820       lchild = backingstore_include_nodes[i].root->children;
821       while(lchild && lchild->next) lchild = lchild->next;
822       if(lchild) {
823         lchild->next = node->children;
824         if(node->children) node->children->prev = lchild;
825       }
826       else
827         backingstore_include_nodes[i].root->children = node->children;
828       for(n=node->children; n; n = n->next) {
829         n->parent = backingstore_include_nodes[i].root; /* this gets mapped right back, just for clarity */
830         n->doc = backingstore_include_nodes[i].doc;
831       }
832       backingstore_include_nodes[i].old_children = NULL;
833       node->children = backingstore_include_nodes[i].root->children;
834       for(n=node->children; n; n = n->next) {
835         n->parent = backingstore_include_nodes[i].insertion_point;
836         n->parent->last = n;
837       }
838     }
839     else {
840       noitL(noit_error, "Could not load: '%s'\n", infile);
841       rv = -1;
842     }
843     free(infile);
844   }
845   if(mix_ctxt) xmlXPathFreeContext(mix_ctxt);
846   mix_ctxt = xmlXPathNewContext(doc);
847   backingstore_include_cnt = cnt;
848   noitL(noit_debug, "Processed %d backing stores.\n", backingstore_include_cnt);
849
850  includes:
851   if(pobj) xmlXPathFreeObject(pobj);
852   *include_cnt = 0;
853   pobj = xmlXPathEval((xmlChar *)"//include[@file]", mix_ctxt);
854   if(!pobj) goto out;
855   if(pobj->type != XPATH_NODESET) goto out;
856   if(xmlXPathNodeSetIsEmpty(pobj->nodesetval)) goto out;
857   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
858   if(cnt > 0) {
859     include_nodes = calloc(cnt, sizeof(*include_nodes));
860     if (master) {
861       config_include_nodes = include_nodes;
862     }
863     else {
864       inc_node->children = include_nodes;
865     }
866     for (i=0; i < cnt; i++) {
867       include_nodes[i].child_count = -1;
868     }
869   }
870   for(i=0; i<cnt; i++) {
871     char *path, *infile, *snippet, *ro;
872     node = xmlXPathNodeSetItem(pobj->nodesetval, i);
873     path = (char *)xmlGetProp(node, (xmlChar *)"file");
874     if(!path) continue;
875     snippet = (char *)xmlGetProp(node, (xmlChar *)"snippet");
876     include_nodes[i].snippet = (snippet && strcmp(snippet, "false"));
877     if(snippet) xmlFree(snippet);
878     if(*path == '/') infile = strdup(path);
879     else {
880       char *cp;
881       infile = malloc(PATH_MAX);
882       strlcpy(infile, parentfile, PATH_MAX);
883       for(cp = infile + strlen(infile) - 1; cp > infile; cp--) {
884         if(*cp == '/') { *cp = '\0'; break; }
885         else *cp = '\0';
886       }
887       strlcat(infile, "/", PATH_MAX);
888       strlcat(infile, path, PATH_MAX);
889     }
890     xmlFree(path);
891     ro = (char *)xmlGetProp(node, (xmlChar *)"readonly");
892     if (ro && !strcmp(ro, "true")) include_nodes[i].ro = 1;
893     if (ro) xmlFree(ro);
894     if (include_nodes[i].snippet)
895       include_nodes[i].doc = xmlParseEntity(infile);
896     else
897       include_nodes[i].doc = xmlReadFile(infile, "utf8", XML_PARSE_NOENT);
898     if((include_nodes[i].doc) || (include_nodes[i].snippet)) {
899       xmlNodePtr n;
900       noit_conf_magic_mix(infile, include_nodes[i].doc, &(include_nodes[i]));
901       strncpy(include_nodes[i].path, infile, sizeof(include_nodes[i].path));
902       include_nodes[i].insertion_point = node;
903       include_nodes[i].root = xmlDocGetRootElement(include_nodes[i].doc);
904       include_nodes[i].old_children = node->children;
905       if (!include_nodes[i].snippet)
906         node->children = include_nodes[i].root->children;
907       else
908         node->children = include_nodes[i].root;
909       for(n=node->children; n; n = n->next)
910         n->parent = include_nodes[i].insertion_point;
911     }
912     else {
913       noitL(noit_error, "Could not load: '%s'\n", infile);
914       rv = -1;
915     }
916     free(infile);
917   }
918   *include_cnt = cnt;
919   noitL(noit_debug, "Processed %d includes\n", *include_cnt);
920  out:
921   if(pobj) xmlXPathFreeObject(pobj);
922   if(mix_ctxt) xmlXPathFreeContext(mix_ctxt);
923   return rv;
924 }
925
926 static int noit_conf_load_internal(const char *path) {
927   int rv = 0;
928   xmlDocPtr new_config;
929   xmlNodePtr root;
930   new_config = xmlParseFile(path);
931   if(new_config) {
932     root = xmlDocGetRootElement(new_config);
933     if(root_node_name) free(root_node_name);
934     root_node_name = strdup((char *)root->name);
935
936     if(master_config) {
937       /* separate all includes */
938       noit_conf_magic_separate();
939       xmlFreeDoc(master_config);
940     }
941     if(xpath_ctxt) xmlXPathFreeContext(xpath_ctxt);
942
943     master_config = new_config;
944     /* mixin all the includes */
945     if(noit_conf_magic_mix(path, master_config, NULL)) rv = -1;
946
947     xpath_ctxt = xmlXPathNewContext(master_config);
948     if(path != master_config_file)
949       if(realpath(path, master_config_file) == NULL)
950         noitL(noit_error, "realpath failed: %s\n", strerror(errno));
951     noit_conf_mark_changed();
952     return rv;
953   }
954   rv = -1;
955   return rv;
956 }
957 int noit_conf_load(const char *path) {
958   int rv;
959   XML2LOG(noit_error);
960   rv = noit_conf_load_internal(path);
961   XML2LOG(xml_debug);
962   return rv;
963 }
964
965 char *noit_conf_config_filename() {
966   return strdup(master_config_file);
967 }
968
969 int noit_conf_xml_xpath(xmlDocPtr *mc, xmlXPathContextPtr *xp) {
970   if(mc) *mc = master_config;
971   if(xp) *xp = xpath_ctxt;
972   return 0;
973 }
974 int noit_conf_save(const char *path) {
975   return -1;
976 }
977
978 void noit_conf_get_elements_into_hash(noit_conf_section_t section,
979                                       const char *path,
980                                       noit_hash_table *table,
981                                       const char *namespace) {
982   int i, cnt;
983   xmlXPathObjectPtr pobj = NULL;
984   xmlXPathContextPtr current_ctxt;
985   xmlNodePtr current_node = (xmlNodePtr)section;
986   xmlNodePtr node;
987
988   current_ctxt = xpath_ctxt;
989   if(current_node) {
990     current_ctxt = xmlXPathNewContext(master_config);
991     current_ctxt->node = current_node;
992   }
993   pobj = xmlXPathEval((xmlChar *)path, current_ctxt);
994   if(!pobj) goto out;
995   if(pobj->type != XPATH_NODESET) goto out;
996   if(xmlXPathNodeSetIsEmpty(pobj->nodesetval)) goto out;
997   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
998   for(i=0; i<cnt; i++) {
999     const xmlChar *name;
1000     int freename = 0;
1001     char *value;
1002     node = xmlXPathNodeSetItem(pobj->nodesetval, i);
1003     if(namespace && node->ns && !strcmp((char *)node->ns->prefix, namespace)) {
1004       name = node->name;
1005       if(!strcmp((char *)name, "value")) {
1006         name = xmlGetProp(node, (xmlChar *)"name");
1007         if(!name) name = node->name;
1008         else freename = 1;
1009       }
1010       value = (char *)xmlXPathCastNodeToString(node);
1011       noit_hash_replace(table,
1012                         strdup((char *)name), strlen((char *)name),
1013                         strdup(value), free, free);
1014       xmlFree(value);
1015     }
1016     else if(!namespace && !node->ns) {
1017       name = node->name;
1018       if(!strcmp((char *)name, "value")) {
1019         name = xmlGetProp(node, (xmlChar *)"name");
1020         if(!name) name = node->name;
1021         else freename = 1;
1022       }
1023       value = (char *)xmlXPathCastNodeToString(node);
1024       noit_hash_replace(table,
1025                         strdup((char *)name), strlen((char *)name),
1026                         strdup(value), free, free);
1027       xmlFree(value);
1028     }
1029     if(freename) xmlFree((void *)name);
1030   }
1031  out:
1032   if(pobj) xmlXPathFreeObject(pobj);
1033   if(current_ctxt && current_ctxt != xpath_ctxt)
1034     xmlXPathFreeContext(current_ctxt);
1035 }
1036 void noit_conf_get_into_hash(noit_conf_section_t section,
1037                              const char *path,
1038                              noit_hash_table *table,
1039                              const char *namespace) {
1040   int cnt;
1041   xmlXPathObjectPtr pobj = NULL;
1042   xmlXPathContextPtr current_ctxt;
1043   xmlNodePtr current_node = (xmlNodePtr)section;
1044   xmlNodePtr node, parent_node;
1045   char xpath_expr[1024];
1046   char *inheritid;
1047
1048   current_ctxt = xpath_ctxt;
1049   if(current_node) {
1050     current_ctxt = xmlXPathNewContext(master_config);
1051     current_ctxt->node = current_node;
1052   }
1053   if(path[0] == '/')
1054     strlcpy(xpath_expr, path, sizeof(xpath_expr));
1055   else
1056     snprintf(xpath_expr, sizeof(xpath_expr),
1057              "ancestor-or-self::node()/%s", path);
1058   pobj = xmlXPathEval((xmlChar *)xpath_expr, current_ctxt);
1059   if(!pobj) goto out;
1060   if(pobj->type != XPATH_NODESET) goto out;
1061   if(xmlXPathNodeSetIsEmpty(pobj->nodesetval)) goto out;
1062   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
1063   /* These are in the order of root to leaf
1064    * We want to recurse... apply:
1065    *   1. our parent's config
1066    *   2. our "inherit" config if it exists.
1067    *   3. our config.
1068    */
1069   node = xmlXPathNodeSetItem(pobj->nodesetval, cnt-1);
1070   /* 1. */
1071   if(cnt > 1 && node) {
1072     parent_node = xmlXPathNodeSetItem(pobj->nodesetval, cnt-2);
1073     if(parent_node != current_node)
1074       noit_conf_get_into_hash(parent_node, (const char *)node->name, table, namespace);
1075   }
1076   /* 2. */
1077   inheritid = (char *)xmlGetProp(node, (xmlChar *)"inherit");
1078   if(inheritid) {
1079     snprintf(xpath_expr, sizeof(xpath_expr), "//*[@id=\"%s\"]", inheritid);
1080     noit_conf_get_into_hash(NULL, xpath_expr, table, namespace);
1081     xmlFree(inheritid);
1082   }
1083   /* 3. */
1084   noit_conf_get_elements_into_hash(node, "*", table, namespace);
1085
1086  out:
1087   if(pobj) xmlXPathFreeObject(pobj);
1088   if(current_ctxt && current_ctxt != xpath_ctxt)
1089     xmlXPathFreeContext(current_ctxt);
1090 }
1091 noit_hash_table *noit_conf_get_namespaced_hash(noit_conf_section_t section,
1092                                                const char *path, const char *ns) {
1093   noit_hash_table *table = NULL;
1094
1095   table = calloc(1, sizeof(*table));
1096   noit_conf_get_into_hash(section, path, table, ns);
1097   if(table->size == 0) {
1098     noit_hash_destroy(table, free, free);
1099     free(table);
1100     table = NULL;
1101   }
1102   return table;
1103 }
1104 noit_hash_table *noit_conf_get_hash(noit_conf_section_t section,
1105                                     const char *path) {
1106   noit_hash_table *table = NULL;
1107
1108   table = calloc(1, sizeof(*table));
1109   noit_conf_get_into_hash(section, path, table, NULL);
1110   return table;
1111 }
1112 noit_conf_section_t noit_conf_get_section(noit_conf_section_t section,
1113                                           const char *path) {
1114   noit_conf_section_t subsection = NULL;
1115   xmlXPathObjectPtr pobj = NULL;
1116   xmlXPathContextPtr current_ctxt;
1117   xmlNodePtr current_node = (xmlNodePtr)section;
1118
1119   current_ctxt = xpath_ctxt;
1120   if(current_node) {
1121     current_ctxt = xmlXPathNewContext(master_config);
1122     current_ctxt->node = current_node;
1123   }
1124   pobj = xmlXPathEval((xmlChar *)path, current_ctxt);
1125   if(!pobj) goto out;
1126   if(pobj->type != XPATH_NODESET) goto out;
1127   if(xmlXPathNodeSetIsEmpty(pobj->nodesetval)) goto out;
1128   subsection = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
1129  out:
1130   if(pobj) xmlXPathFreeObject(pobj);
1131   if(current_ctxt && current_ctxt != xpath_ctxt)
1132     xmlXPathFreeContext(current_ctxt);
1133   return subsection;
1134 }
1135 noit_conf_section_t *noit_conf_get_sections(noit_conf_section_t section,
1136                                             const char *path,
1137                                             int *cnt) {
1138   int i;
1139   noit_conf_section_t *sections = NULL;
1140   xmlXPathObjectPtr pobj = NULL;
1141   xmlXPathContextPtr current_ctxt;
1142   xmlNodePtr current_node = (xmlNodePtr)section;
1143
1144   *cnt = 0;
1145   current_ctxt = xpath_ctxt;
1146   if(current_node) {
1147     current_ctxt = xmlXPathNewContext(master_config);
1148     current_ctxt->node = current_node;
1149   }
1150   pobj = xmlXPathEval((xmlChar *)path, current_ctxt);
1151   if(!pobj) goto out;
1152   if(pobj->type != XPATH_NODESET) goto out;
1153   if(xmlXPathNodeSetIsEmpty(pobj->nodesetval)) goto out;
1154   *cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
1155   sections = calloc(*cnt, sizeof(*sections));
1156   for(i=0; i<*cnt; i++)
1157     sections[i] = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
1158  out:
1159   if(pobj) xmlXPathFreeObject(pobj);
1160   if(current_ctxt && current_ctxt != xpath_ctxt)
1161     xmlXPathFreeContext(current_ctxt);
1162   return sections;
1163 }
1164 int noit_conf_remove_section(noit_conf_section_t section) {
1165   if (!section) return -1;
1166   xmlNodePtr node = (xmlNodePtr) section;
1167   xmlUnlinkNode(node);
1168   xmlFreeNode(node);
1169   noit_conf_mark_changed();
1170   return 0;
1171 }
1172 int _noit_conf_get_string(noit_conf_section_t section, xmlNodePtr *vnode,
1173                           const char *path, char **value) {
1174   const char *str, *interest;
1175   char fullpath[1024];
1176   int rv = 1, i;
1177   xmlXPathObjectPtr pobj = NULL;
1178   xmlXPathContextPtr current_ctxt;
1179   xmlNodePtr current_node = (xmlNodePtr)section;
1180
1181   current_ctxt = xpath_ctxt;
1182   if(current_node) {
1183     current_ctxt = xmlXPathNewContext(master_config);
1184     current_ctxt->node = current_node;
1185   }
1186   pobj = xmlXPathEval((xmlChar *)path, current_ctxt);
1187   if(pobj) {
1188     xmlNodePtr node;
1189     switch(pobj->type) {
1190       case XPATH_NODESET:
1191         if(xmlXPathNodeSetIsEmpty(pobj->nodesetval)) goto fallback;
1192         i = xmlXPathNodeSetGetLength(pobj->nodesetval);
1193         node = xmlXPathNodeSetItem(pobj->nodesetval, i-1);
1194         if(vnode) *vnode = node;
1195         *value = (char *)xmlXPathCastNodeToString(node);
1196         break;
1197       default:
1198         *value = (char *)xmlXPathCastToString(pobj);
1199     }
1200     goto found;
1201   }
1202  fallback:
1203   interest = path;
1204   if(*interest != '/' && current_node) {
1205     xmlChar *basepath = xmlGetNodePath(current_node);
1206     snprintf(fullpath, sizeof(fullpath), "%s/%s", (char *)basepath, path);
1207     free(basepath);
1208     interest = fullpath;
1209   }
1210   if(noit_hash_retr_str(&_compiled_fallback,
1211                         interest, strlen(interest), &str)) {
1212     *value = (char *)xmlStrdup((xmlChar *)str);
1213     goto found;
1214   }
1215   rv = 0;
1216  found:
1217   if(pobj) xmlXPathFreeObject(pobj);
1218   if(current_ctxt && current_ctxt != xpath_ctxt)
1219     xmlXPathFreeContext(current_ctxt);
1220   return rv;
1221 }
1222 int noit_conf_get_uuid(noit_conf_section_t section,
1223                        const char *path, uuid_t out) {
1224   char *str;
1225   if(_noit_conf_get_string(section,NULL,path,&str)) {
1226     if(uuid_parse(str, out) == 0) return 1;
1227     return 0;
1228   }
1229   return 0;
1230 }
1231 int noit_conf_get_string(noit_conf_section_t section,
1232                          const char *path, char **value) {
1233   char *str;
1234   if(_noit_conf_get_string(section,NULL,path,&str)) {
1235     *value = strdup(str);
1236     xmlFree(str);
1237     return 1;
1238   }
1239   return 0;
1240 }
1241 int noit_conf_get_stringbuf(noit_conf_section_t section,
1242                             const char *path, char *buf, int len) {
1243   char *str;
1244   if(_noit_conf_get_string(section,NULL,path,&str)) {
1245     strlcpy(buf, str, len);
1246     xmlFree(str);
1247     return 1;
1248   }
1249   return 0;
1250 }
1251 int noit_conf_set_string(noit_conf_section_t section,
1252                          const char *path, const char *value) {
1253   xmlNodePtr current_node = (xmlNodePtr)section;
1254   if(strchr(path, '/')) return 0;
1255   if(path[0] == '@') {
1256     xmlSetProp(current_node, (xmlChar *)path+1, (xmlChar *)value);
1257     CONF_DIRTY(current_node);
1258   }
1259   else {
1260     xmlNodePtr child_node;
1261     if(value) {
1262       child_node = xmlNewTextChild(current_node, NULL, (xmlChar *)path, (xmlChar *)value);
1263     }
1264     else {
1265       child_node = xmlNewChild(current_node, NULL, (xmlChar *)path, NULL);
1266     }
1267     CONF_DIRTY(child_node);
1268   }
1269   noit_conf_mark_changed();
1270   if(noit_conf_write_file(NULL) != 0)
1271     noitL(noit_error, "local config write failed\n");
1272   return 1;
1273 }
1274 int noit_conf_string_to_int(const char *str) {
1275   int base = 10;
1276   if(!str) return 0;
1277   if(str[0] == '0') {
1278     if(str[1] == 'x') base = 16;
1279     else base = 8;
1280   }
1281   return strtol(str, NULL, base);
1282 }
1283 int noit_conf_get_int(noit_conf_section_t section,
1284                       const char *path, int *value) {
1285   char *str;
1286   if(_noit_conf_get_string(section,NULL,path,&str)) {
1287     *value = (int)noit_conf_string_to_int(str);
1288     xmlFree(str);
1289     return 1;
1290   }
1291   return 0;
1292 }
1293 int noit_conf_get_int64(noit_conf_section_t section,
1294                         const char *path, int64_t *value) {
1295   char *str;
1296   if(_noit_conf_get_string(section,NULL,path,&str)) {
1297     *value = strtoll(str, NULL, 10);
1298     xmlFree(str);
1299     return 1;
1300   }
1301   return 0;
1302 }
1303 int noit_conf_set_int(noit_conf_section_t section,
1304                       const char *path, int value) {
1305   char buffer[32];
1306   snprintf(buffer, 32, "%d", value);
1307   return noit_conf_set_string(section,path,buffer);
1308 }
1309 float noit_conf_string_to_float(const char *str) {
1310   if(!str) return 0.0;
1311   return atof(str);
1312 }
1313 int noit_conf_get_float(noit_conf_section_t section,
1314                         const char *path, float *value) {
1315   char *str;
1316   if(_noit_conf_get_string(section,NULL,path,&str)) {
1317     *value = noit_conf_string_to_float(str);
1318     xmlFree(str);
1319     return 1;
1320   }
1321   return 0;
1322 }
1323 int noit_conf_set_float(noit_conf_section_t section,
1324                         const char *path, float value) {
1325   char buffer[32];
1326   snprintf(buffer, 32, "%f", value);
1327   return noit_conf_set_string(section,path,buffer);
1328 }
1329 noit_boolean noit_conf_string_to_boolean(const char *str) {
1330   if(!str) return noit_false;
1331   if(!strcasecmp(str, "true") || !strcasecmp(str, "on")) return noit_true;
1332   return noit_false;
1333 }
1334 int noit_conf_get_boolean(noit_conf_section_t section,
1335                           const char *path, noit_boolean *value) {
1336   char *str;
1337   if(_noit_conf_get_string(section,NULL,path,&str)) {
1338     *value = noit_conf_string_to_boolean(str);
1339     xmlFree(str);
1340     return 1;
1341   }
1342   return 0;
1343 }
1344 int noit_conf_set_boolean(noit_conf_section_t section,
1345                           const char *path, noit_boolean value) {
1346   if(value == noit_true)
1347     return noit_conf_set_string(section,path,"true");
1348   return noit_conf_set_string(section,path,"false");
1349 }
1350
1351 int noit_conf_should_resolve_targets(noit_boolean *should_resolve) {
1352   static int inited = 0, cached_rv;;
1353   static noit_boolean cached_should_resolve;
1354   if(!inited) {
1355     cached_rv = noit_conf_get_boolean(NULL, "//checks/@resolve_targets",
1356                                       &cached_should_resolve);
1357     inited = 1;
1358   }
1359   *should_resolve = cached_should_resolve;
1360   return cached_rv;
1361 }
1362
1363 struct config_line_vstr {
1364   char *buff;
1365   int raw_len;
1366   int len;
1367   int allocd;
1368   enum { CONFIG_RAW = 0, CONFIG_COMPRESSED, CONFIG_B64 } target, encoded;
1369 };
1370 static int
1371 noit_config_log_write_xml(void *vstr, const char *buffer, int len) {
1372   struct config_line_vstr *clv = vstr;
1373   assert(clv->encoded == CONFIG_RAW);
1374   if(!clv->buff) {
1375     clv->allocd = 8192;
1376     clv->buff = malloc(clv->allocd);
1377   }
1378   while(len + clv->len > clv->allocd) {
1379     char *newbuff;
1380     int newsize = clv->allocd;
1381     newsize <<= 1;
1382     newbuff = realloc(clv->buff, newsize);
1383     if(!newbuff) {
1384       return -1;
1385     }
1386     clv->allocd = newsize;
1387     clv->buff = newbuff;
1388   }
1389   memcpy(clv->buff + clv->len, buffer, len);
1390   clv->len += len;
1391   return len;
1392 }
1393 static int
1394 noit_config_log_close_xml(void *vstr) {
1395   struct config_line_vstr *clv = vstr;
1396   uLong initial_dlen, dlen;
1397   char *compbuff, *b64buff;
1398
1399   if(clv->buff == NULL) {
1400     clv->encoded = clv->target;
1401     return 0;
1402   }
1403   clv->raw_len = clv->len;
1404   assert(clv->encoded == CONFIG_RAW);
1405   if(clv->encoded == clv->target) return 0;
1406
1407   /* Compress */
1408   initial_dlen = dlen = compressBound(clv->len);
1409   compbuff = malloc(initial_dlen);
1410   if(!compbuff) return -1;
1411   if(Z_OK != compress2((Bytef *)compbuff, &dlen,
1412                        (Bytef *)clv->buff, clv->len, 9)) {
1413     noitL(noit_error, "Error compressing config for transmission.\n");
1414     free(compbuff);
1415     return -1;
1416   }
1417   free(clv->buff);
1418   clv->buff = compbuff;
1419   clv->allocd = initial_dlen;
1420   clv->len = dlen;
1421   clv->encoded = CONFIG_COMPRESSED;
1422   if(clv->encoded == clv->target) return 0;
1423
1424   /* Encode */
1425   initial_dlen = ((clv->len + 2) / 3) * 4;
1426   b64buff = malloc(initial_dlen);
1427   dlen = noit_b64_encode((unsigned char *)clv->buff, clv->len,
1428                          b64buff, initial_dlen);
1429   if(dlen == 0) {
1430     free(b64buff);
1431     return -1;
1432   }
1433   free(clv->buff);
1434   clv->buff = b64buff;
1435   clv->allocd = initial_dlen;
1436   clv->len = dlen;
1437   clv->encoded = CONFIG_B64;
1438   if(clv->encoded == clv->target) return 0;
1439   return -1;
1440 }
1441
1442 int
1443 noit_conf_reload(noit_console_closure_t ncct,
1444                  int argc, char **argv,
1445                  noit_console_state_t *state, void *closure) {
1446   XML2CONSOLE(ncct);
1447   if(noit_conf_load_internal(master_config_file)) {
1448     XML2LOG(xml_debug);
1449     nc_printf(ncct, "error loading config\n");
1450     return -1;
1451   }
1452   XML2LOG(xml_debug);
1453   return 0;
1454 }
1455 int
1456 noit_conf_write_terminal(noit_console_closure_t ncct,
1457                          int argc, char **argv,
1458                          noit_console_state_t *state, void *closure) {
1459   xmlOutputBufferPtr out;
1460   xmlCharEncodingHandlerPtr enc;
1461   enc = xmlGetCharEncodingHandler(XML_CHAR_ENCODING_UTF8);
1462   out = xmlOutputBufferCreateIO(noit_console_write_xml,
1463                                 noit_console_close_xml,
1464                                 ncct, enc);
1465   noit_conf_kansas_city_shuffle_undo(config_include_nodes, config_include_cnt);
1466   xmlSaveFormatFileTo(out, master_config, "utf8", 1);
1467   write_out_include_files(config_include_nodes, config_include_cnt);
1468   noit_conf_kansas_city_shuffle_redo(config_include_nodes, config_include_cnt);
1469   return 0;
1470 }
1471 int
1472 noit_conf_write_file_console(noit_console_closure_t ncct,
1473                              int argc, char **argv,
1474                              noit_console_state_t *state, void *closure) {
1475   int rv;
1476   char *err = NULL;
1477   rv = noit_conf_write_file(&err);
1478   nc_printf(ncct, "%s\n", err);
1479   if(err) free(err);
1480   return rv;
1481 }
1482 int
1483 noit_conf_write_file(char **err) {
1484   int fd, len;
1485   char master_file_tmp[PATH_MAX];
1486   char errstr[1024];
1487   xmlOutputBufferPtr out;
1488   xmlCharEncodingHandlerPtr enc;
1489   struct stat st;
1490   mode_t mode = 0640; /* the default */
1491   uid_t uid = 0;
1492   gid_t gid = 0;
1493
1494   if(stat(master_config_file, &st) == 0) {
1495     mode = st.st_mode;
1496     uid = st.st_uid;
1497     gid = st.st_gid;
1498   }
1499   snprintf(master_file_tmp, sizeof(master_file_tmp),
1500            "%s.tmp", master_config_file);
1501   unlink(master_file_tmp);
1502   fd = open(master_file_tmp, O_CREAT|O_EXCL|O_WRONLY|NE_O_CLOEXEC, mode);
1503   if(fd < 0) {
1504     snprintf(errstr, sizeof(errstr), "Failed to open tmp file: %s",
1505              strerror(errno));
1506     if(err) *err = strdup(errstr);
1507     return -1;
1508   }
1509   fchown(fd, uid, gid);
1510
1511   enc = xmlGetCharEncodingHandler(XML_CHAR_ENCODING_UTF8);
1512   out = xmlOutputBufferCreateFd(fd, enc);
1513   if(!out) {
1514     close(fd);
1515     unlink(master_file_tmp);
1516     if(err) *err = strdup("internal error: OutputBufferCreate failed");
1517     return -1;
1518   }
1519   noit_conf_kansas_city_shuffle_undo(config_include_nodes, config_include_cnt);
1520   noit_conf_shatter_write(master_config);
1521   len = xmlSaveFormatFileTo(out, master_config, "utf8", 1);
1522   noit_conf_shatter_postwrite(master_config);
1523   write_out_include_files(config_include_nodes, config_include_cnt);
1524   noit_conf_kansas_city_shuffle_redo(config_include_nodes, config_include_cnt);
1525   close(fd);
1526   if(len <= 0) {
1527     if(err) *err = strdup("internal error: writing to tmp file failed.");
1528     return -1;
1529   }
1530   if(rename(master_file_tmp, master_config_file) != 0) {
1531     snprintf(errstr, sizeof(errstr), "Failed to replace file: %s",
1532              strerror(errno));
1533     if(err) *err = strdup(errstr);
1534     return -1;
1535   }
1536   snprintf(errstr, sizeof(errstr), "%d bytes written.", len);
1537   if(err) *err = strdup(errstr);
1538   return 0;
1539 }
1540 char *
1541 noit_conf_xml_in_mem(size_t *len) {
1542   struct config_line_vstr *clv;
1543   xmlOutputBufferPtr out;
1544   xmlCharEncodingHandlerPtr enc;
1545   char *rv;
1546
1547   clv = calloc(1, sizeof(*clv));
1548   clv->target = CONFIG_RAW;
1549   enc = xmlGetCharEncodingHandler(XML_CHAR_ENCODING_UTF8);
1550   out = xmlOutputBufferCreateIO(noit_config_log_write_xml,
1551                                 noit_config_log_close_xml,
1552                                 clv, enc);
1553   noit_conf_kansas_city_shuffle_undo(config_include_nodes, config_include_cnt);
1554   xmlSaveFormatFileTo(out, master_config, "utf8", 1);
1555   write_out_include_files(config_include_nodes, config_include_cnt);
1556   noit_conf_kansas_city_shuffle_redo(config_include_nodes, config_include_cnt);
1557   if(clv->encoded != CONFIG_RAW) {
1558     noitL(noit_error, "Error logging configuration\n");
1559     if(clv->buff) free(clv->buff);
1560     free(clv);
1561     return NULL;
1562   }
1563   rv = clv->buff;
1564   *len = clv->len;
1565   free(clv);
1566   return rv;
1567 }
1568
1569 int
1570 noit_conf_write_log() {
1571   static u_int32_t last_write_gen = 0;
1572   static noit_log_stream_t config_log = NULL;
1573   struct timeval __now;
1574   xmlOutputBufferPtr out;
1575   xmlCharEncodingHandlerPtr enc;
1576   struct config_line_vstr *clv;
1577   noit_boolean notify_only = noit_false;
1578   const char *v;
1579   SETUP_LOG(config, return -1);
1580   if(!N_L_S_ON(config_log)) return 0;
1581
1582   v = noit_log_stream_get_property(config_log, "notify_only");
1583   if(v && (!strcmp(v, "on") || !strcmp(v, "true"))) notify_only = noit_true;
1584
1585   /* We know we haven't changed */
1586   if(last_write_gen == __config_gen) return 0;
1587   gettimeofday(&__now, NULL);
1588
1589   if(notify_only) {
1590     noitL(config_log, "n\t%lu.%03lu\t%d\t\n",
1591           (unsigned long int)__now.tv_sec,
1592           (unsigned long int)__now.tv_usec / 1000UL, 0);
1593     last_write_gen = __config_gen;
1594     return 0;
1595   }
1596
1597   clv = calloc(1, sizeof(*clv));
1598   clv->target = CONFIG_B64;
1599   enc = xmlGetCharEncodingHandler(XML_CHAR_ENCODING_UTF8);
1600   out = xmlOutputBufferCreateIO(noit_config_log_write_xml,
1601                                 noit_config_log_close_xml,
1602                                 clv, enc);
1603   noit_conf_kansas_city_shuffle_undo(config_include_nodes, config_include_cnt);
1604   xmlSaveFormatFileTo(out, master_config, "utf8", 1);
1605   write_out_include_files(config_include_nodes, config_include_cnt);
1606   noit_conf_kansas_city_shuffle_redo(config_include_nodes, config_include_cnt);
1607   if(clv->encoded != CONFIG_B64) {
1608     noitL(noit_error, "Error logging configuration\n");
1609     if(clv->buff) free(clv->buff);
1610     free(clv);
1611     return -1;
1612   }
1613   noitL(config_log, "n\t%lu.%03lu\t%d\t%.*s\n",
1614         (unsigned long int)__now.tv_sec,
1615         (unsigned long int)__now.tv_usec / 1000UL, clv->raw_len,
1616         clv->len, clv->buff);
1617   free(clv->buff);
1618   free(clv);
1619   last_write_gen = __config_gen;
1620   return 0;
1621 }
1622
1623 struct log_rotate_crutch {
1624   noit_log_stream_t ls;
1625   int seconds;
1626   int retain_seconds;
1627   size_t max_size;
1628   ssize_t retain_size;
1629 };
1630
1631 static int
1632 noit_conf_log_cull(eventer_t e, int mask, void *closure,
1633                    struct timeval *now) {
1634   struct log_rotate_crutch *lrc = closure;
1635   if(!(mask & EVENTER_ASYNCH_WORK)) return 0;
1636   noit_log_stream_cull(lrc->ls, lrc->retain_seconds, lrc->retain_size);
1637   return 0;
1638 }
1639 static void
1640 schedule_background_log_cull(struct log_rotate_crutch *lrc) {
1641   eventer_t e;
1642   if(lrc->retain_size < 0 && lrc->retain_seconds < 0) return;
1643   e = eventer_alloc();
1644   e->closure = lrc;
1645   e->callback = noit_conf_log_cull;
1646   e->mask = EVENTER_ASYNCH;
1647   eventer_add(e);
1648 }
1649 static int
1650 noit_conf_log_rotate_size(eventer_t e, int mask, void *closure,
1651                           struct timeval *now) {
1652   struct log_rotate_crutch *lrc = closure;
1653   if(noit_log_stream_written(lrc->ls) > lrc->max_size) {
1654     noit_log_stream_rename(lrc->ls, NOIT_LOG_RENAME_AUTOTIME);
1655     noit_log_stream_reopen(lrc->ls);
1656     schedule_background_log_cull(lrc);
1657   }
1658   /* Yes the 5 is arbitrary, but this is cheap */
1659   eventer_add_in_s_us(noit_conf_log_rotate_size, closure, 5, 0);
1660   return 0;
1661 }
1662 static int
1663 noit_conf_log_rotate_time(eventer_t e, int mask, void *closure,
1664                           struct timeval *now) {
1665   struct timeval lnow;
1666   eventer_t newe;
1667   struct log_rotate_crutch *lrc = closure;
1668
1669   if(now) {
1670     noit_log_stream_rename(lrc->ls, NOIT_LOG_RENAME_AUTOTIME);
1671     noit_log_stream_reopen(lrc->ls);
1672     schedule_background_log_cull(lrc);
1673   }
1674  
1675   newe = eventer_alloc();
1676   newe->closure = closure;
1677   if(!now) { gettimeofday(&lnow, NULL); now = &lnow; }
1678   if(e)
1679     memcpy(&newe->whence, &e->whence, sizeof(newe->whence));
1680   else if(now) {
1681     memcpy(&newe->whence, now, sizeof(newe->whence));
1682     newe->whence.tv_sec = (newe->whence.tv_sec / lrc->seconds) * lrc->seconds;
1683   }
1684   newe->whence.tv_sec += lrc->seconds;
1685   newe->mask = EVENTER_TIMER;
1686   newe->callback = noit_conf_log_rotate_time;
1687   eventer_add(newe);
1688   return 0;
1689 }
1690 int
1691 noit_conf_log_init_rotate(const char *toplevel, noit_boolean validate) {
1692   int i, cnt = 0, max_time, max_size, retain_seconds = -1, retain_size = -1, rv = 0;
1693   noit_conf_section_t *log_configs;
1694   char path[256];
1695
1696   snprintf(path, sizeof(path), "/%s/logs//log", toplevel);
1697   log_configs = noit_conf_get_sections(NULL, path, &cnt);
1698   noitL(noit_debug, "Found %d %s stanzas\n", cnt, path);
1699   for(i=0; i<cnt; i++) {
1700     noit_log_stream_t ls;
1701     char name[256];
1702
1703     if(!noit_conf_get_stringbuf(log_configs[i],
1704                                 "ancestor-or-self::node()/@name",
1705                                 name, sizeof(name))) {
1706       noitL(noit_error, "log section %d does not have a name attribute\n", i+1);
1707       if(validate) { rv = -1; break; }
1708       else exit(-2);
1709     }
1710     ls = noit_log_stream_find(name);
1711     if(!ls) continue;
1712
1713     if(noit_conf_get_int(log_configs[i],   
1714                          "ancestor-or-self::node()/@rotate_seconds",
1715                          &max_time) && max_time) {
1716       struct log_rotate_crutch *lrc;
1717       if(max_time < 600) {
1718         fprintf(stderr, "rotate_seconds must be >= 600s (10 minutes)\n");
1719         if(validate) { rv = -1; break; }
1720         else exit(-2);
1721       }
1722       noit_conf_get_int(log_configs[i],
1723                         "ancestor-or-self::node()/@retain_seconds",
1724                         &retain_seconds);
1725       if(!validate) {
1726         lrc = calloc(1, sizeof(*lrc));
1727         lrc->ls = ls;
1728         lrc->seconds = max_time;
1729         lrc->retain_size = -1;
1730         lrc->retain_seconds = retain_seconds;
1731         noit_conf_log_rotate_time(NULL, EVENTER_TIMER, lrc, NULL);
1732       }
1733     }
1734
1735     if(noit_conf_get_int(log_configs[i],   
1736                          "ancestor-or-self::node()/@rotate_bytes",
1737                          &max_size) && max_size) {
1738       struct log_rotate_crutch *lrc;
1739       if(max_size < 102400) {
1740         fprintf(stderr, "rotate_bytes must be >= 102400 (100k)\n");
1741         if(validate) { rv = -1; break; }
1742         else exit(-2);
1743       }
1744       noit_conf_get_int(log_configs[i],
1745                         "ancestor-or-self::node()/@retain_bytes",
1746                         &retain_size);
1747       if(!validate) {
1748         lrc = calloc(1, sizeof(*lrc));
1749         lrc->ls = ls;
1750         lrc->max_size = max_size;
1751         lrc->retain_seconds = -1;
1752         lrc->retain_size = retain_size;
1753         noit_conf_log_rotate_size(NULL, EVENTER_TIMER, lrc, NULL);
1754       }
1755     }
1756   }
1757   free(log_configs);
1758   return rv;
1759 }
1760 void
1761 noit_conf_log_init(const char *toplevel) {
1762   int i, cnt = 0, o, ocnt = 0;
1763   noit_conf_section_t *log_configs, *outlets;
1764   char path[256];
1765
1766   snprintf(path, sizeof(path), "/%s/logs//log", toplevel);
1767   log_configs = noit_conf_get_sections(NULL, path, &cnt);
1768   noitL(noit_debug, "Found %d %s stanzas\n", cnt, path);
1769   for(i=0; i<cnt; i++) {
1770     int flags;
1771     noit_log_stream_t ls;
1772     char name[256], type[256], path[256];
1773     noit_hash_table *config;
1774     noit_boolean disabled, debug, timestamps, facility;
1775
1776     if(!noit_conf_get_stringbuf(log_configs[i],
1777                                 "ancestor-or-self::node()/@name",
1778                                 name, sizeof(name))) {
1779       noitL(noit_error, "log section %d does not have a name attribute\n", i+1);
1780       exit(-1);
1781     }
1782     if(!noit_conf_get_stringbuf(log_configs[i],
1783                                 "ancestor-or-self::node()/@type",
1784                                 type, sizeof(type))) {
1785       type[0] = '\0';
1786     }
1787     if(!noit_conf_get_stringbuf(log_configs[i],
1788                                 "ancestor-or-self::node()/@path",
1789                                 path, sizeof(path))) {
1790       path[0] = '\0';
1791     }
1792     config = noit_conf_get_hash(log_configs[i],
1793                                 "ancestor-or-self::node()/config");
1794     ls = noit_log_stream_new(name, type[0] ? type : NULL,
1795                              path[0] ? path : NULL, NULL, config);
1796     if(!ls) {
1797       fprintf(stderr, "Error configuring log: %s[%s:%s]\n", name, type, path);
1798       exit(-1);
1799     }
1800
1801     flags = noit_log_stream_get_flags(ls);
1802     if(noit_conf_get_boolean(log_configs[i],
1803                              "ancestor-or-self::node()/@disabled",
1804                              &disabled)) {
1805       if(disabled) flags &= ~NOIT_LOG_STREAM_ENABLED;
1806       else         flags |= NOIT_LOG_STREAM_ENABLED;
1807     }
1808     if(noit_conf_get_boolean(log_configs[i],
1809                              "ancestor-or-self::node()/@debug",
1810                              &debug)) {
1811       if(debug) flags |= NOIT_LOG_STREAM_DEBUG;
1812       else      flags &= ~NOIT_LOG_STREAM_DEBUG;
1813     }
1814     if(noit_conf_get_boolean(log_configs[i],
1815                              "ancestor-or-self::node()/@timestamps",
1816                              &timestamps)) {
1817       if(timestamps) flags |= NOIT_LOG_STREAM_TIMESTAMPS;
1818       else           flags &= ~NOIT_LOG_STREAM_TIMESTAMPS;
1819     }
1820     if(noit_conf_get_boolean(log_configs[i],
1821                              "ancestor-or-self::node()/@facility",
1822                              &facility)) {
1823       if(facility) flags |= NOIT_LOG_STREAM_FACILITY;
1824       else         flags &= ~NOIT_LOG_STREAM_FACILITY;
1825     }
1826     noit_log_stream_set_flags(ls, flags);
1827
1828     outlets = noit_conf_get_sections(log_configs[i],
1829                                      "ancestor-or-self::node()/outlet", &ocnt);
1830     noitL(noit_debug, "Found %d outlets for log '%s'\n", ocnt, name);
1831
1832     for(o=0; o<ocnt; o++) {
1833       noit_log_stream_t outlet;
1834       char oname[256];
1835       noit_conf_get_stringbuf(outlets[o], "@name",
1836                               oname, sizeof(oname));
1837       outlet = noit_log_stream_find(oname);
1838       if(!outlet) {
1839         fprintf(stderr, "Cannot find outlet '%s' for %s[%s:%s]\n", oname,
1840               name, type, path);
1841         exit(-1);
1842       }
1843       else
1844         noit_log_stream_add_stream(ls, outlet);
1845     }
1846     if(outlets) free(outlets);
1847   }
1848   if(log_configs) free(log_configs);
1849   if(noit_conf_log_init_rotate(toplevel, noit_true)) exit(-1);
1850 }
1851
1852 static void
1853 conf_t_userdata_free(void *data) {
1854   noit_conf_t_userdata_t *info = data;
1855   if(info) {
1856     if(info->path) free(info->path);
1857     free(info);
1858   }
1859 }
1860
1861 static int
1862 noit_console_state_conf_terminal(noit_console_closure_t ncct,
1863                                  int argc, char **argv,
1864                                  noit_console_state_t *state, void *closure) {
1865   noit_conf_t_userdata_t *info;
1866   if(argc) {
1867     nc_printf(ncct, "extra arguments not expected.\n");
1868     return -1;
1869   }
1870   info = calloc(1, sizeof(*info));
1871   info->path = strdup("/");
1872   noit_console_userdata_set(ncct, NOIT_CONF_T_USERDATA, info,
1873                             conf_t_userdata_free);
1874   noit_console_state_push_state(ncct, state);
1875   noit_console_state_init(ncct);
1876   return 0;
1877 }
1878 static int
1879 noit_console_config_section(noit_console_closure_t ncct,
1880                             int argc, char **argv,
1881                             noit_console_state_t *state, void *closure) {
1882   const char *err = "internal error";
1883   char *path, xpath[1024];
1884   noit_conf_t_userdata_t *info;
1885   xmlXPathObjectPtr pobj = NULL;
1886   xmlXPathContextPtr xpath_ctxt = NULL;
1887   xmlNodePtr node = NULL, newnode;
1888   vpsized_int delete = (vpsized_int)closure;
1889
1890   noit_conf_xml_xpath(NULL, &xpath_ctxt);
1891   if(argc != 1) {
1892     nc_printf(ncct, "requires one argument\n");
1893     return -1;
1894   }
1895   if(strchr(argv[0], '/')) {
1896     nc_printf(ncct, "invalid section name\n");
1897     return -1;
1898   }
1899   if(!strcmp(argv[0], "check") ||
1900      !strcmp(argv[0], "noit") ||
1901      !strcmp(argv[0], "filterset") ||
1902      !strcmp(argv[0], "config")) {
1903     nc_printf(ncct, "%s is reserved.\n", argv[0]);
1904     return -1;
1905   }
1906   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
1907   if(!strcmp(info->path, "/")) {
1908     nc_printf(ncct, "manipulation of toplevel section disallowed\n");
1909     return -1;
1910   }
1911
1912   if(delete) {
1913     /* We cannot delete if we have checks */
1914     snprintf(xpath, sizeof(xpath), "/%s%s/%s//check", root_node_name,
1915              info->path, argv[0]);
1916     pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
1917     if(!pobj || pobj->type != XPATH_NODESET ||
1918        !xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
1919       err = "cannot delete section, has checks";
1920       goto bad;
1921     }
1922     if(pobj) xmlXPathFreeObject(pobj);
1923   }
1924
1925   snprintf(xpath, sizeof(xpath), "/%s%s/%s", root_node_name,
1926            info->path, argv[0]);
1927   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
1928   if(!pobj || pobj->type != XPATH_NODESET) {
1929     err = "internal error: cannot detect section";
1930     goto bad;
1931   }
1932   if(!delete && !xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
1933     if(xmlXPathNodeSetGetLength(pobj->nodesetval) == 1) {
1934       node = xmlXPathNodeSetItem(pobj->nodesetval, 0);
1935       free(info->path);
1936       info->path = strdup((char *)xmlGetNodePath(node) +
1937                           1 + strlen(root_node_name));
1938       goto cdout;
1939     }
1940     err = "cannot create section";
1941     goto bad;
1942   }
1943   if(delete && xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
1944     err = "no such section";
1945     goto bad;
1946   }
1947   if(delete) {
1948     node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
1949     if(node) {
1950       CONF_REMOVE(node);
1951       xmlUnlinkNode(node);
1952       noit_conf_mark_changed();
1953     }
1954     return 0;
1955   }
1956   if(pobj) xmlXPathFreeObject(pobj);
1957   pobj = NULL;
1958
1959   if(!strcmp(argv[0],"include")) {
1960     err = "include is a reserved section name";
1961     goto bad;
1962   }
1963   path = strcmp(info->path, "/") ? info->path : "";
1964   snprintf(xpath, sizeof(xpath), "/%s%s", root_node_name, path);
1965   pobj = xmlXPathEval((xmlChar *)xpath, xpath_ctxt);
1966   if(!pobj || pobj->type != XPATH_NODESET ||
1967      xmlXPathNodeSetGetLength(pobj->nodesetval) != 1) {
1968     err = "path invalid?";
1969     goto bad;
1970   }
1971   node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
1972   if((newnode = xmlNewChild(node, NULL, (xmlChar *)argv[0], NULL)) != NULL) {
1973     noit_conf_mark_changed();
1974     free(info->path);
1975     info->path = strdup((char *)xmlGetNodePath(newnode) + 1 +
1976                         strlen(root_node_name));
1977   }
1978   else {
1979     err = "failed to create section";
1980     goto bad;
1981   }
1982  cdout:
1983   if(pobj) xmlXPathFreeObject(pobj);
1984   return 0;
1985  bad:
1986   if(pobj) xmlXPathFreeObject(pobj);
1987   nc_printf(ncct, "%s\n", err);
1988   return -1;
1989 }
1990
1991 int
1992 noit_console_generic_show(noit_console_closure_t ncct,
1993                           int argc, char **argv,
1994                           noit_console_state_t *state, void *closure) {
1995   int i, cnt, titled = 0, cliplen = 0;
1996   const char *path = "", *basepath = NULL;
1997   char xpath[1024];
1998   noit_conf_t_userdata_t *info = NULL;
1999   xmlXPathObjectPtr pobj = NULL;
2000   xmlXPathContextPtr xpath_ctxt = NULL, current_ctxt;
2001   xmlDocPtr master_config = NULL;
2002   xmlNodePtr node = NULL;
2003
2004   noit_conf_xml_xpath(&master_config, &xpath_ctxt);
2005   if(argc > 1) {
2006     nc_printf(ncct, "too many arguments\n");
2007     return -1;
2008   }
2009
2010   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
2011   if(info && info->path) path = basepath = info->path;
2012   if(!info && argc == 0) {
2013     nc_printf(ncct, "argument required when not in configuration mode\n");
2014     return -1;
2015   }
2016
2017   if(argc == 1) path = argv[0];
2018   if(!basepath) basepath = path;
2019
2020   /* { / } is a special case */
2021   if(!strcmp(basepath, "/")) basepath = "";
2022   if(!strcmp(path, "/")) path = "";
2023
2024   if(!master_config) {
2025     nc_printf(ncct, "no config\n");
2026     return -1;
2027   }
2028
2029   /* { / } is the only path that will end with a /
2030    * in XPath { / / * } means something _entirely different than { / * }
2031    * Ever notice how it is hard to describe xpath in C comments?
2032    */
2033   /* We don't want to show the root node */
2034   cliplen = strlen(root_node_name) + 2; /* /name/ */
2035
2036   /* If we are in configuration mode
2037    * and we are without an argument or the argument is absolute,
2038    * clip the current path off */
2039   if(info && (argc == 0 || path[0] != '/')) cliplen += strlen(basepath);
2040   if(!path[0] || path[0] == '/') /* base only, or absolute path requested */
2041     snprintf(xpath, sizeof(xpath), "/%s%s/@*", root_node_name, path);
2042   else
2043     snprintf(xpath, sizeof(xpath), "/%s%s/%s/@*", root_node_name,
2044              basepath, path);
2045
2046   current_ctxt = xpath_ctxt;
2047   pobj = xmlXPathEval((xmlChar *)xpath, current_ctxt);
2048   if(!pobj || pobj->type != XPATH_NODESET) {
2049     nc_printf(ncct, "no such object\n");
2050     goto bad;
2051   }
2052   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
2053   titled = 0;
2054   for(i=0; i<cnt; i++) {
2055     node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
2056     if(node->children && node->children == xmlGetLastChild(node) &&
2057       xmlNodeIsText(node->children)) {
2058       if(!titled++) nc_printf(ncct, "== Section Settings ==\n");
2059       nc_printf(ncct, "%s: %s\n", xmlGetNodePath(node) + cliplen,
2060                 xmlXPathCastNodeToString(node->children));
2061     }
2062   }
2063   xmlXPathFreeObject(pobj);
2064
2065   /* _shorten string_ turning last { / @ * } to { / * } */
2066   if(!path[0] || path[0] == '/') /* base only, or absolute path requested */
2067     snprintf(xpath, sizeof(xpath), "/%s%s/*", root_node_name, path);
2068   else
2069     snprintf(xpath, sizeof(xpath), "/%s%s/%s/*",
2070              root_node_name, basepath, path);
2071   pobj = xmlXPathEval((xmlChar *)xpath, current_ctxt);
2072   if(!pobj || pobj->type != XPATH_NODESET) {
2073     nc_printf(ncct, "no such object\n");
2074     goto bad;
2075   }
2076   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
2077   titled = 0;
2078   for(i=0; i<cnt; i++) {
2079     node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
2080     if(!(node->children && node->children == xmlGetLastChild(node) &&
2081          xmlNodeIsText(node->children))) {
2082       if(!titled++) nc_printf(ncct, "== Subsections ==\n");
2083       nc_printf(ncct, "%s\n", xmlGetNodePath(node) + cliplen);
2084     }
2085   }
2086   xmlXPathFreeObject(pobj);
2087   return 0;
2088  bad:
2089   if(pobj) xmlXPathFreeObject(pobj);
2090   return -1;
2091 }
2092 int
2093 noit_console_config_cd(noit_console_closure_t ncct,
2094                        int argc, char **argv,
2095                        noit_console_state_t *state, void *closure) {
2096   const char *err = "internal error";
2097   char *path = NULL, xpath[1024];
2098   noit_conf_t_userdata_t *info;
2099   xmlXPathObjectPtr pobj = NULL;
2100   xmlXPathContextPtr xpath_ctxt = NULL, current_ctxt;
2101   xmlNodePtr node = NULL;
2102   char *dest;
2103
2104   noit_conf_xml_xpath(NULL, &xpath_ctxt);
2105   if(argc != 1 && !closure) {
2106     nc_printf(ncct, "requires one argument\n");
2107     return -1;
2108   }
2109   dest = argc ? argv[0] : (char *)closure;
2110   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
2111   if(dest[0] == '/')
2112     snprintf(xpath, sizeof(xpath), "/%s%s", root_node_name, dest);
2113   else {
2114     snprintf(xpath, sizeof(xpath), "/%s%s/%s", root_node_name,
2115              info->path, dest);
2116   }
2117   if(xpath[strlen(xpath)-1] == '/') xpath[strlen(xpath)-1] = '\0';
2118
2119   current_ctxt = xpath_ctxt;
2120   pobj = xmlXPathEval((xmlChar *)xpath, current_ctxt);
2121   if(!pobj || pobj->type != XPATH_NODESET ||
2122      xmlXPathNodeSetIsEmpty(pobj->nodesetval)) {
2123     err = "no such section";
2124     goto bad;
2125   }
2126   if(xmlXPathNodeSetGetLength(pobj->nodesetval) > 1) {
2127     err = "ambiguous section";
2128     goto bad;
2129   }
2130
2131   node = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
2132   if(!node) {
2133     err = "internal XML error";
2134     goto bad;
2135   }
2136   if(!strcmp((char *)node->name, "check") ||
2137      !strcmp((char *)node->name, "noit") ||
2138      !strcmp((char *)node->name, "filterset") ||
2139      !strcmp((char *)node->name, "config")) {
2140     err = "reserved word";
2141     goto bad;
2142   }
2143   path = (char *)xmlGetNodePath(node);
2144   if(strlen(path) < strlen(root_node_name) + 1 ||
2145      strncmp(path + 1, root_node_name, strlen(root_node_name)) ||
2146      (path[strlen(root_node_name) + 1] != '/' &&
2147       path[strlen(root_node_name) + 1] != '\0')) {
2148     err = "new path outside out tree";
2149     goto bad;
2150   }
2151   free(info->path);
2152   if(!strcmp(path + 1, root_node_name))
2153     info->path = strdup("/");
2154   else {
2155     char *xmlpath = (char *)xmlGetNodePath(node);
2156     info->path = strdup(xmlpath + 1 +
2157                         strlen(root_node_name));
2158     free(xmlpath);
2159   }
2160
2161   free(path);
2162   if(pobj) xmlXPathFreeObject(pobj);
2163   if(closure) noit_console_state_pop(ncct, argc, argv, NULL, NULL);
2164   return 0;
2165  bad:
2166   if(path) free(path);
2167   if(pobj) xmlXPathFreeObject(pobj);
2168   nc_printf(ncct, "%s [%s]\n", err, xpath);
2169   return -1;
2170 }
2171
2172 char *
2173 conf_t_prompt(EditLine *el) {
2174   noit_console_closure_t ncct;
2175   noit_conf_t_userdata_t *info;
2176   static char *tl = "noit(conf)# ";
2177   static char *pfmt = "noit(conf:%s%s)# ";
2178   int path_len, max_len;
2179
2180   el_get(el, EL_USERDATA, (void *)&ncct);
2181   if(!ncct) return tl;
2182   info = noit_console_userdata_get(ncct, NOIT_CONF_T_USERDATA);
2183   if(!info) return tl;
2184
2185   path_len = strlen(info->path);
2186   max_len = sizeof(info->prompt) - (strlen(pfmt) - 4 /* %s%s */) - 1 /* \0 */;
2187   if(path_len > max_len)
2188     snprintf(info->prompt, sizeof(info->prompt),
2189              pfmt, "...", info->path + path_len - max_len + 3 /* ... */);
2190   else
2191     snprintf(info->prompt, sizeof(info->prompt), pfmt, "", info->path);
2192   return info->prompt;
2193 }
2194
2195 #define NEW_STATE(a) (a) = noit_console_state_alloc()
2196 #define ADD_CMD(a,cmd,func,ac,ss,c) \
2197   noit_console_state_add_cmd((a), \
2198     NCSCMD(cmd, func, ac, ss, c))
2199 #define DELEGATE_CMD(a,cmd,ac,ss) \
2200   noit_console_state_add_cmd((a), \
2201     NCSCMD(cmd, noit_console_state_delegate, ac, ss, NULL))
2202
2203 void noit_console_conf_init() {
2204   noit_console_state_t *tl, *_conf_state, *_conf_t_state,
2205                        *_write_state, *_unset_state;
2206
2207   tl = noit_console_state_initial();
2208
2209   /* write <terimal|memory|file> */
2210   NEW_STATE(_write_state);
2211   ADD_CMD(_write_state, "terminal", noit_conf_write_terminal, NULL, NULL, NULL);
2212   ADD_CMD(_write_state, "file", noit_conf_write_file_console, NULL, NULL, NULL);
2213   /* write memory?  It's to a file, but I like router syntax */
2214   ADD_CMD(_write_state, "memory", noit_conf_write_file_console, NULL, NULL, NULL);
2215
2216   NEW_STATE(_unset_state);
2217   ADD_CMD(_unset_state, "section",
2218           noit_console_config_section, NULL, NULL, (void *)1);
2219
2220   NEW_STATE(_conf_t_state);
2221   _conf_t_state->console_prompt_function = conf_t_prompt;
2222   noit_console_state_add_cmd(_conf_t_state, &console_command_exit);
2223
2224   ADD_CMD(_conf_t_state, "ls", noit_console_generic_show, NULL, NULL, NULL);
2225   ADD_CMD(_conf_t_state, "cd", noit_console_config_cd, NULL, NULL, NULL);
2226   ADD_CMD(_conf_t_state, "section",
2227           noit_console_config_section, NULL, NULL, (void *)0);
2228
2229   DELEGATE_CMD(_conf_t_state, "write",
2230                noit_console_opt_delegate, _write_state);
2231   DELEGATE_CMD(_conf_t_state, "no", noit_console_opt_delegate, _unset_state);
2232
2233   NEW_STATE(_conf_state);
2234   ADD_CMD(_conf_state, "terminal",
2235           noit_console_state_conf_terminal, NULL, _conf_t_state, NULL);
2236
2237   ADD_CMD(tl, "configure",
2238           noit_console_state_delegate, noit_console_opt_delegate,
2239           _conf_state, NULL);
2240   ADD_CMD(tl, "write",
2241           noit_console_state_delegate, noit_console_opt_delegate,
2242           _write_state, NULL);
2243 }
2244
Note: See TracBrowser for help on using the browser.