root/src/noit_conf.c

Revision 7010bc5b07e615c0314ff1c0ebb27bdf8420cca3, 24.0 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 5 years ago)

robust input validation, refs #171

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2007, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  *       notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  *       copyright notice, this list of conditions and the following
13  *       disclaimer in the documentation and/or other materials provided
14  *       with the distribution.
15  *     * Neither the name OmniTI Computer Consulting, Inc. nor the names
16  *       of its contributors may be used to endorse or promote products
17  *       derived from this software without specific prior written
18  *       permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include "noit_defines.h"
34
35 #include <stdio.h>
36 #include <unistd.h>
37 #include <fcntl.h>
38 #include <errno.h>
39 #include <assert.h>
40 #include <libxml/parser.h>
41 #include <libxml/tree.h>
42 #include <libxml/xpath.h>
43 #include <zlib.h>
44
45 #include "noit_conf.h"
46 #include "noit_check.h"
47 #include "noit_console.h"
48 #include "utils/noit_hash.h"
49 #include "utils/noit_log.h"
50 #include "utils/noit_b64.h"
51
52 /* tmp hash impl, replace this with something nice */
53 static noit_hash_table _tmp_config = NOIT_HASH_EMPTY;
54 static xmlDocPtr master_config = NULL;
55 static char master_config_file[PATH_MAX] = "";
56 static xmlXPathContextPtr xpath_ctxt = NULL;
57
58 /* This is used to notice config changes and journal the config out
59  * using a user-specified function.  It supports allowing multiple config
60  * changed to coalesce so you don't write out 1000 changes in a few seconds.
61  */
62 static u_int32_t __config_gen = 0;
63 static u_int32_t __config_coalesce = 0;
64 static u_int32_t __config_coalesce_time = 0;
65 void noit_conf_coalesce_changes(u_int32_t seconds) {
66   __config_coalesce_time = seconds;
67 }
68 void noit_conf_mark_changed() {
69   /* increment the change counter -- in case anyone cares */
70   __config_gen++;
71   /* reset the coalesce counter.  It is decremented each second and
72    * the journal function fires on a transition from 1 => 0
73    */
74   __config_coalesce = __config_coalesce_time;
75 }
76 struct recurrent_journaler {
77   int (*journal_config)(void *);
78   void *jc_closure;
79 };
80 static int
81 noit_conf_watch_config_and_journal(eventer_t e, int mask, void *closure,
82                                    struct timeval *now) {
83   struct recurrent_journaler *rj = closure;
84   eventer_t newe;
85
86   if(__config_coalesce == 1)
87     rj->journal_config(rj->jc_closure);
88   if(__config_coalesce > 0)
89     __config_coalesce--;
90
91   /* Schedule the same event to fire a second form now */
92   newe = eventer_alloc();
93   gettimeofday(&newe->whence, NULL);
94   newe->whence.tv_sec += 1;
95   newe->mask = EVENTER_TIMER;
96   newe->callback = noit_conf_watch_config_and_journal;
97   newe->closure = closure;
98   eventer_add(newe);
99   return 0;
100 }
101 void
102 noit_conf_watch_and_journal_watchdog(int (*f)(void *), void *c) {
103   struct recurrent_journaler *rj;
104   struct timeval __now;
105   rj = calloc(1, sizeof(*rj));
106   rj->journal_config = f;
107   rj->jc_closure = c;
108   gettimeofday(&__now, NULL);
109   noit_conf_watch_config_and_journal(NULL, EVENTER_TIMER, rj, &__now);
110 }
111
112 static noit_hash_table _compiled_fallback = NOIT_HASH_EMPTY;
113 static struct {
114   const char *key;
115   const char *val;
116 } config_info[] = {
117   /*
118    * These are compile-time fallbacks to be used in the event
119    * that the current running config does not have values for
120    * these config paths.
121    *
122    * PLEASE: keep them alphabetically sorted.
123    */
124   { "/%s/eventer/@implementation", DEFAULT_EVENTER },
125   { "/%s/modules/@directory", MODULES_DIR },
126
127   { NULL, NULL }
128 };
129
130 void noit_conf_xml_error_func(void *ctx, const char *format, ...) {
131   struct timeval __now;
132   noit_log_stream_t ls = ctx;
133   va_list arg;
134   if(!ls) return;
135   va_start(arg, format);
136   gettimeofday(&__now,  NULL);
137   noit_vlog(ls, &__now, __FILE__, __LINE__, format, arg);
138   va_end(arg);
139 }
140 void noit_conf_xml_error_ext_func(void *ctx, xmlErrorPtr err) {
141   struct timeval __now;
142   noit_log_stream_t ls = ctx;
143   if(!ls) return;
144   gettimeofday(&__now,  NULL);
145   if(err->file)
146     noit_log(ls, &__now, err->file, err->line,
147              "XML error [%d/%d] in %s on line %d %s\n",
148              err->domain, err->code, err->file, err->line, err->message);
149   else
150     noit_log(ls, &__now, err->file, err->line,
151              "XML error [%d/%d] %s\n",
152              err->domain, err->code, err->message);
153 }
154
155
156 DECLARE_CHECKER(name)
157 void noit_conf_init(const char *toplevel) {
158   int i;
159   char keystr[256];
160   COMPILE_CHECKER(name, "^[-_\\.:/a-zA-Z0-9]+$");
161   xmlSetGenericErrorFunc(noit_error, noit_conf_xml_error_func);
162   xmlSetStructuredErrorFunc(noit_error, noit_conf_xml_error_ext_func);
163   for(i = 0; config_info[i].key != NULL; i++) {
164     snprintf(keystr, sizeof(keystr), config_info[i].key, toplevel);
165     noit_hash_store(&_compiled_fallback,
166                     strdup(keystr), strlen(keystr),
167                     (void *)strdup(config_info[i].val));
168   }
169   xmlKeepBlanksDefault(0);
170   xmlInitParser();
171   xmlXPathInit();
172 }
173
174 int noit_conf_load(const char *path) {
175   xmlDocPtr new_config;
176   new_config = xmlParseFile(path);
177   if(new_config) {
178     if(master_config) xmlFreeDoc(master_config);
179     if(xpath_ctxt) xmlXPathFreeContext(xpath_ctxt);
180
181     master_config = new_config;
182     xpath_ctxt = xmlXPathNewContext(master_config);
183     if(path != master_config_file)
184       if(realpath(path, master_config_file) == NULL)
185         noitL(noit_error, "realpath failed: %s\n", strerror(errno));
186     noit_conf_mark_changed();
187     return 0;
188   }
189   return -1;
190 }
191
192 char *noit_conf_config_filename() {
193   return strdup(master_config_file);
194 }
195
196 int noit_conf_xml_xpath(xmlDocPtr *mc, xmlXPathContextPtr *xp) {
197   if(mc) *mc = master_config;
198   if(xp) *xp = xpath_ctxt;
199   return 0;
200 }
201 int noit_conf_save(const char *path) {
202   return -1;
203 }
204
205 void noit_conf_get_elements_into_hash(noit_conf_section_t section,
206                                       const char *path,
207                                       noit_hash_table *table) {
208   int i, cnt;
209   xmlXPathObjectPtr pobj = NULL;
210   xmlXPathContextPtr current_ctxt;
211   xmlNodePtr current_node = (xmlNodePtr)section;
212   xmlNodePtr node;
213
214   current_ctxt = xpath_ctxt;
215   if(current_node) {
216     current_ctxt = xmlXPathNewContext(master_config);
217     current_ctxt->node = current_node;
218   }
219   pobj = xmlXPathEval((xmlChar *)path, current_ctxt);
220   if(!pobj) goto out;
221   if(pobj->type != XPATH_NODESET) goto out;
222   if(xmlXPathNodeSetIsEmpty(pobj->nodesetval)) goto out;
223   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
224   for(i=0; i<cnt; i++) {
225     char *value;
226     node = xmlXPathNodeSetItem(pobj->nodesetval, i);
227     value = (char *)xmlXPathCastNodeToString(node);
228     noit_hash_replace(table,
229                       strdup((char *)node->name), strlen((char *)node->name),
230                       strdup(value), free, free);
231     xmlFree(value);
232   }
233  out:
234   if(pobj) xmlXPathFreeObject(pobj);
235   if(current_ctxt && current_ctxt != xpath_ctxt)
236     xmlXPathFreeContext(current_ctxt);
237 }
238 void noit_conf_get_into_hash(noit_conf_section_t section,
239                              const char *path,
240                              noit_hash_table *table) {
241   int cnt;
242   xmlXPathObjectPtr pobj = NULL;
243   xmlXPathContextPtr current_ctxt;
244   xmlNodePtr current_node = (xmlNodePtr)section;
245   xmlNodePtr node, parent_node;
246   char xpath_expr[1024];
247   char *inheritid;
248
249   current_ctxt = xpath_ctxt;
250   if(current_node) {
251     current_ctxt = xmlXPathNewContext(master_config);
252     current_ctxt->node = current_node;
253   }
254   if(path[0] == '/')
255     strlcpy(xpath_expr, path, sizeof(xpath_expr));
256   else
257     snprintf(xpath_expr, sizeof(xpath_expr),
258              "ancestor-or-self::node()/%s", path);
259   pobj = xmlXPathEval((xmlChar *)xpath_expr, current_ctxt);
260   if(!pobj) goto out;
261   if(pobj->type != XPATH_NODESET) goto out;
262   if(xmlXPathNodeSetIsEmpty(pobj->nodesetval)) goto out;
263   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
264   /* These are in the order of root to leaf
265    * We want to recurse... apply:
266    *   1. our parent's config
267    *   2. our "inherit" config if it exists.
268    *   3. our config.
269    */
270   node = xmlXPathNodeSetItem(pobj->nodesetval, cnt-1);
271   /* 1. */
272   if(cnt > 1) {
273     parent_node = xmlXPathNodeSetItem(pobj->nodesetval, cnt-2);
274     if(parent_node != current_node)
275       noit_conf_get_into_hash(parent_node, (const char *)node->name, table);
276   }
277   /* 2. */
278   inheritid = (char *)xmlGetProp(node, (xmlChar *)"inherit");
279   if(inheritid) {
280     snprintf(xpath_expr, sizeof(xpath_expr), "//*[@id=\"%s\"]", inheritid);
281     noit_conf_get_into_hash(NULL, xpath_expr, table);
282   }
283   /* 3. */
284   noit_conf_get_elements_into_hash(node, "*", table);
285
286  out:
287   if(pobj) xmlXPathFreeObject(pobj);
288   if(current_ctxt && current_ctxt != xpath_ctxt)
289     xmlXPathFreeContext(current_ctxt);
290 }
291 noit_hash_table *noit_conf_get_hash(noit_conf_section_t section,
292                                     const char *path) {
293   noit_hash_table *table = NULL;
294
295   table = calloc(1, sizeof(*table));
296   noit_conf_get_into_hash(section, path, table);
297   return table;
298 }
299 noit_conf_section_t noit_conf_get_section(noit_conf_section_t section,
300                                           const char *path) {
301   noit_conf_section_t subsection = NULL;
302   xmlXPathObjectPtr pobj = NULL;
303   xmlXPathContextPtr current_ctxt;
304   xmlNodePtr current_node = (xmlNodePtr)section;
305
306   current_ctxt = xpath_ctxt;
307   if(current_node) {
308     current_ctxt = xmlXPathNewContext(master_config);
309     current_ctxt->node = current_node;
310   }
311   pobj = xmlXPathEval((xmlChar *)path, current_ctxt);
312   if(!pobj) goto out;
313   if(pobj->type != XPATH_NODESET) goto out;
314   if(xmlXPathNodeSetIsEmpty(pobj->nodesetval)) goto out;
315   subsection = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
316  out:
317   if(pobj) xmlXPathFreeObject(pobj);
318   if(current_ctxt && current_ctxt != xpath_ctxt)
319     xmlXPathFreeContext(current_ctxt);
320   return subsection;
321 }
322 noit_conf_section_t *noit_conf_get_sections(noit_conf_section_t section,
323                                             const char *path,
324                                             int *cnt) {
325   int i;
326   noit_conf_section_t *sections = NULL;
327   xmlXPathObjectPtr pobj = NULL;
328   xmlXPathContextPtr current_ctxt;
329   xmlNodePtr current_node = (xmlNodePtr)section;
330
331   *cnt = 0;
332   current_ctxt = xpath_ctxt;
333   if(current_node) {
334     current_ctxt = xmlXPathNewContext(master_config);
335     current_ctxt->node = current_node;
336   }
337   pobj = xmlXPathEval((xmlChar *)path, current_ctxt);
338   if(!pobj) goto out;
339   if(pobj->type != XPATH_NODESET) goto out;
340   if(xmlXPathNodeSetIsEmpty(pobj->nodesetval)) goto out;
341   *cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
342   sections = calloc(*cnt, sizeof(*sections));
343   for(i=0; i<*cnt; i++)
344     sections[i] = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
345  out:
346   if(pobj) xmlXPathFreeObject(pobj);
347   if(current_ctxt && current_ctxt != xpath_ctxt)
348     xmlXPathFreeContext(current_ctxt);
349   return sections;
350 }
351 int _noit_conf_get_string(noit_conf_section_t section, xmlNodePtr *vnode,
352                           const char *path, char **value) {
353   const char *str;
354   int i, rv = 1;
355   xmlXPathObjectPtr pobj = NULL;
356   xmlXPathContextPtr current_ctxt;
357   xmlNodePtr current_node = (xmlNodePtr)section;
358
359   current_ctxt = xpath_ctxt;
360   if(current_node) {
361     current_ctxt = xmlXPathNewContext(master_config);
362     current_ctxt->node = current_node;
363   }
364   pobj = xmlXPathEval((xmlChar *)path, current_ctxt);
365   if(pobj) {
366     xmlNodePtr node;
367     switch(pobj->type) {
368       case XPATH_NODESET:
369         if(xmlXPathNodeSetIsEmpty(pobj->nodesetval)) goto fallback;
370         i = xmlXPathNodeSetGetLength(pobj->nodesetval);
371         node = xmlXPathNodeSetItem(pobj->nodesetval, i-1);
372         if(vnode) *vnode = node;
373         *value = (char *)xmlXPathCastNodeToString(node);
374         break;
375       default:
376         *value = (char *)xmlXPathCastToString(pobj);
377     }
378     goto found;
379   }
380  fallback:
381   if(noit_hash_retr_str(&_compiled_fallback,
382                         path, strlen(path), &str)) {
383     *value = (char *)xmlStrdup((xmlChar *)str);
384     goto found;
385   }
386   rv = 0;
387  found:
388   if(pobj) xmlXPathFreeObject(pobj);
389   if(current_ctxt && current_ctxt != xpath_ctxt)
390     xmlXPathFreeContext(current_ctxt);
391   return rv;
392 }
393 int noit_conf_get_uuid(noit_conf_section_t section,
394                        const char *path, uuid_t out) {
395   char *str;
396   if(_noit_conf_get_string(section,NULL,path,&str)) {
397     if(uuid_parse(str, out) == 0) return 1;
398     return 0;
399   }
400   return 0;
401 }
402 int noit_conf_get_string(noit_conf_section_t section,
403                          const char *path, char **value) {
404   char *str;
405   if(_noit_conf_get_string(section,NULL,path,&str)) {
406     *value = strdup(str);
407     xmlFree(str);
408     return 1;
409   }
410   return 0;
411 }
412 int noit_conf_get_stringbuf(noit_conf_section_t section,
413                             const char *path, char *buf, int len) {
414   char *str;
415   if(_noit_conf_get_string(section,NULL,path,&str)) {
416     strlcpy(buf, str, len);
417     xmlFree(str);
418     return 1;
419   }
420   return 0;
421 }
422 int noit_conf_set_string(noit_conf_section_t section,
423                          const char *path, const char *value) {
424   noit_hash_replace(&_tmp_config,
425                     strdup(path), strlen(path), (void *)strdup(value),
426                     free, free);
427   return 1;
428 }
429 int noit_conf_string_to_int(const char *str) {
430   int base = 10;
431   if(!str) return 0;
432   if(str[0] == '0') {
433     if(str[1] == 'x') base = 16;
434     else base = 8;
435   }
436   return strtol(str, NULL, base);
437 }
438 int noit_conf_get_int(noit_conf_section_t section,
439                       const char *path, int *value) {
440   char *str;
441   if(_noit_conf_get_string(section,NULL,path,&str)) {
442     *value = (int)noit_conf_string_to_int(str);
443     xmlFree(str);
444     return 1;
445   }
446   return 0;
447 }
448 int noit_conf_set_int(noit_conf_section_t section,
449                       const char *path, int value) {
450   char buffer[32];
451   snprintf(buffer, 32, "%d", value);
452   return noit_conf_set_string(section,path,buffer);
453 }
454 float noit_conf_string_to_float(const char *str) {
455   if(!str) return 0.0;
456   return atof(str);
457 }
458 int noit_conf_get_float(noit_conf_section_t section,
459                         const char *path, float *value) {
460   char *str;
461   if(_noit_conf_get_string(section,NULL,path,&str)) {
462     *value = noit_conf_string_to_float(str);
463     xmlFree(str);
464     return 1;
465   }
466   return 0;
467 }
468 int noit_conf_set_float(noit_conf_section_t section,
469                         const char *path, float value) {
470   char buffer[32];
471   snprintf(buffer, 32, "%f", value);
472   return noit_conf_set_string(section,path,buffer);
473 }
474 noit_boolean noit_conf_string_to_boolean(const char *str) {
475   if(!str) return noit_false;
476   if(!strcasecmp(str, "true") || !strcasecmp(str, "on")) return noit_true;
477   return noit_false;
478 }
479 int noit_conf_get_boolean(noit_conf_section_t section,
480                           const char *path, noit_boolean *value) {
481   char *str;
482   if(_noit_conf_get_string(section,NULL,path,&str)) {
483     *value = noit_conf_string_to_boolean(str);
484     xmlFree(str);
485     return 1;
486   }
487   return 0;
488 }
489 int noit_conf_set_boolean(noit_conf_section_t section,
490                           const char *path, noit_boolean value) {
491   if(value == noit_true)
492     return noit_conf_set_string(section,path,"true");
493   return noit_conf_set_string(section,path,"false");
494 }
495
496 struct config_line_vstr {
497   char *buff;
498   int raw_len;
499   int len;
500   int allocd;
501   enum { CONFIG_RAW = 0, CONFIG_COMPRESSED, CONFIG_B64 } target, encoded;
502 };
503 static int
504 noit_config_log_write_xml(void *vstr, const char *buffer, int len) {
505   struct config_line_vstr *clv = vstr;
506   assert(clv->encoded == CONFIG_RAW);
507   if(!clv->buff) {
508     clv->allocd = 8192;
509     clv->buff = malloc(clv->allocd);
510   }
511   while(len + clv->len > clv->allocd) {
512     char *newbuff;
513     int newsize = clv->allocd;
514     newsize <<= 1;
515     newbuff = realloc(clv->buff, newsize);
516     if(!newbuff) {
517       return -1;
518     }
519     clv->allocd = newsize;
520     clv->buff = newbuff;
521   }
522   memcpy(clv->buff + clv->len, buffer, len);
523   clv->len += len;
524   return len;
525 }
526 static int
527 noit_config_log_close_xml(void *vstr) {
528   struct config_line_vstr *clv = vstr;
529   uLong initial_dlen, dlen;
530   char *compbuff, *b64buff;
531
532   if(clv->buff == NULL) {
533     clv->encoded = clv->target;
534     return 0;
535   }
536   clv->raw_len = clv->len;
537   assert(clv->encoded == CONFIG_RAW);
538   if(clv->encoded == clv->target) return 0;
539
540   /* Compress */
541   initial_dlen = dlen = compressBound(clv->len);
542   compbuff = malloc(initial_dlen);
543   if(!compbuff) return -1;
544   if(Z_OK != compress2((Bytef *)compbuff, &dlen,
545                        (Bytef *)clv->buff, clv->len, 9)) {
546     noitL(noit_error, "Error compressing config for transmission.\n");
547     free(compbuff);
548     return -1;
549   }
550   free(clv->buff);
551   clv->buff = compbuff;
552   clv->allocd = initial_dlen;
553   clv->len = dlen;
554   clv->encoded = CONFIG_COMPRESSED;
555   if(clv->encoded == clv->target) return 0;
556
557   /* Encode */
558   initial_dlen = ((clv->len + 2) / 3) * 4;
559   b64buff = malloc(initial_dlen);
560   dlen = noit_b64_encode((unsigned char *)clv->buff, clv->len,
561                          b64buff, initial_dlen);
562   if(dlen == 0) {
563     free(b64buff);
564     return -1;
565   }
566   free(clv->buff);
567   clv->buff = b64buff;
568   clv->allocd = initial_dlen;
569   clv->len = dlen;
570   clv->encoded = CONFIG_B64;
571   if(clv->encoded == clv->target) return 0;
572   return -1;
573 }
574
575 int
576 noit_conf_reload(noit_console_closure_t ncct,
577                  int argc, char **argv,
578                  noit_console_state_t *state, void *closure) {
579   if(noit_conf_load(master_config_file)) {
580     nc_printf(ncct, "error loading config\n");
581     return -1;
582   }
583   return 0;
584 }
585 int
586 noit_conf_write_terminal(noit_console_closure_t ncct,
587                          int argc, char **argv,
588                          noit_console_state_t *state, void *closure) {
589   xmlOutputBufferPtr out;
590   xmlCharEncodingHandlerPtr enc;
591   enc = xmlGetCharEncodingHandler(XML_CHAR_ENCODING_UTF8);
592   out = xmlOutputBufferCreateIO(noit_console_write_xml,
593                                 noit_console_close_xml,
594                                 ncct, enc);
595   xmlSaveFormatFileTo(out, master_config, "utf8", 1);
596   return 0;
597 }
598 int
599 noit_conf_write_file(noit_console_closure_t ncct,
600                      int argc, char **argv,
601                      noit_console_state_t *state, void *closure) {
602   int fd, len;
603   char master_file_tmp[PATH_MAX];
604   xmlOutputBufferPtr out;
605   xmlCharEncodingHandlerPtr enc;
606
607   snprintf(master_file_tmp, sizeof(master_file_tmp),
608            "%s.tmp", master_config_file);
609   unlink(master_file_tmp);
610   fd = open(master_file_tmp, O_CREAT|O_EXCL|O_WRONLY, 0640);
611   if(fd < 0) {
612     nc_printf(ncct, "Failed to open tmp file: %s\n", strerror(errno));
613     return -1;
614   }
615   enc = xmlGetCharEncodingHandler(XML_CHAR_ENCODING_UTF8);
616   out = xmlOutputBufferCreateFd(fd, enc);
617   if(!out) {
618     close(fd);
619     unlink(master_file_tmp);
620     nc_printf(ncct, "internal error: OutputBufferCreate failed\n");
621     return -1;
622   }
623   len = xmlSaveFormatFileTo(out, master_config, "utf8", 1);
624   close(fd);
625   if(len <= 0) {
626     nc_printf(ncct, "internal error: writing to tmp file failed.\n");
627     return -1;
628   }
629   if(rename(master_file_tmp, master_config_file) != 0) {
630     nc_printf(ncct, "Failed to replace file: %s\n", strerror(errno));
631     return -1;
632   }
633   nc_printf(ncct, "%d bytes written.\n", len);
634   return 0;
635 }
636 char *
637 noit_conf_xml_in_mem(size_t *len) {
638   struct config_line_vstr *clv;
639   xmlOutputBufferPtr out;
640   xmlCharEncodingHandlerPtr enc;
641   char *rv;
642
643   clv = calloc(1, sizeof(*clv));
644   clv->target = CONFIG_RAW;
645   enc = xmlGetCharEncodingHandler(XML_CHAR_ENCODING_UTF8);
646   out = xmlOutputBufferCreateIO(noit_config_log_write_xml,
647                                 noit_config_log_close_xml,
648                                 clv, enc);
649   xmlSaveFormatFileTo(out, master_config, "utf8", 1);
650   if(clv->encoded != CONFIG_RAW) {
651     noitL(noit_error, "Error logging configuration\n");
652     if(clv->buff) free(clv->buff);
653     free(clv);
654     return NULL;
655   }
656   rv = clv->buff;
657   *len = clv->len;
658   free(clv);
659   return rv;
660 }
661
662 int
663 noit_conf_write_log() {
664   static u_int32_t last_write_gen = 0;
665   static noit_log_stream_t config_log = NULL;
666   struct timeval __now;
667   xmlOutputBufferPtr out;
668   xmlCharEncodingHandlerPtr enc;
669   struct config_line_vstr *clv;
670   SETUP_LOG(config, return -1);
671
672   /* We know we haven't changed */
673   if(last_write_gen == __config_gen) return 0;
674
675   gettimeofday(&__now, NULL);
676   clv = calloc(1, sizeof(*clv));
677   clv->target = CONFIG_B64;
678   enc = xmlGetCharEncodingHandler(XML_CHAR_ENCODING_UTF8);
679   out = xmlOutputBufferCreateIO(noit_config_log_write_xml,
680                                 noit_config_log_close_xml,
681                                 clv, enc);
682   xmlSaveFormatFileTo(out, master_config, "utf8", 1);
683   if(clv->encoded != CONFIG_B64) {
684     noitL(noit_error, "Error logging configuration\n");
685     if(clv->buff) free(clv->buff);
686     free(clv);
687     return -1;
688   }
689   noitL(config_log, "n\t%lu.%03lu\t%d\t%.*s\n",
690         __now.tv_sec, __now.tv_usec / 1000UL, clv->raw_len,
691         clv->len, clv->buff);
692   free(clv->buff);
693   free(clv);
694   last_write_gen = __config_gen;
695   return 0;
696 }
697
698 void
699 noit_conf_log_init(const char *toplevel) {
700   int i, cnt = 0, o, ocnt = 0;
701   noit_conf_section_t *log_configs, *outlets;
702   char path[256];
703
704   snprintf(path, sizeof(path), "/%s/logs//log", toplevel);
705   log_configs = noit_conf_get_sections(NULL, path, &cnt);
706   noitL(noit_stderr, "Found %d %s stanzas\n", cnt, path);
707   for(i=0; i<cnt; i++) {
708     noit_log_stream_t ls;
709     char name[256], type[256], path[256];
710     noit_hash_table *config;
711     noit_boolean disabled;
712     noit_boolean debug;
713
714     if(!noit_conf_get_stringbuf(log_configs[i],
715                                 "ancestor-or-self::node()/@name",
716                                 name, sizeof(name))) {
717       noitL(noit_error, "log section %d does not have a name attribute\n", i+1);
718       exit(-1);
719     }
720     if(!noit_conf_get_stringbuf(log_configs[i],
721                                 "ancestor-or-self::node()/@type",
722                                 type, sizeof(type))) {
723       type[0] = '\0';
724     }
725     if(!noit_conf_get_stringbuf(log_configs[i],
726                                 "ancestor-or-self::node()/@path",
727                                 path, sizeof(path))) {
728       path[0] = '\0';
729     }
730     config = noit_conf_get_hash(log_configs[i],
731                                 "ancestor-or-self::node()/config/*");
732     ls = noit_log_stream_new(name, type[0] ? type : NULL,
733                              path[0] ? path : NULL, NULL, config);
734     if(!ls) {
735       fprintf(stderr, "Error configuring log: %s[%s:%s]\n", name, type, path);
736       exit(-1);
737     }
738
739     if(noit_conf_get_boolean(log_configs[i],
740                              "ancestor-or-self::node()/@disabled",
741                              &disabled) && disabled)
742       ls->enabled = 0;
743      
744     if(noit_conf_get_boolean(log_configs[i],
745                              "ancestor-or-self::node()/@debug",
746                              &debug) && debug)
747       ls->debug = 1;
748      
749     outlets = noit_conf_get_sections(log_configs[i],
750                                      "ancestor-or-self::node()/outlet", &ocnt);
751     noitL(noit_debug, "Found %d outlets for log '%s'\n", ocnt, name);
752
753     for(o=0; o<ocnt; o++) {
754       noit_log_stream_t outlet;
755       char oname[256];
756       noit_conf_get_stringbuf(outlets[o], "@name",
757                               oname, sizeof(oname));
758       outlet = noit_log_stream_find(oname);
759       if(!outlet) {
760         fprintf(stderr, "Cannot find outlet '%s' for %s[%s:%s]\n", oname,
761               name, type, path);
762         exit(-1);
763       }
764       else
765         noit_log_stream_add_stream(ls, outlet);
766     }
767     if(outlets) free(outlets);
768   }
769   if(log_configs) free(log_configs);
770 }
Note: See TracBrowser for help on using the browser.