root/src/noit_conf.c

Revision adfa45097790eccac3ff2976afdb1342874bb351, 70.5 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 5 hours ago)

Allow for xpath printing from the config -- for scripts

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