root/src/noit_conf.c

Revision 8ad126b7390d75e24ecc859efb01570b01bbcdc1, 36.8 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 5 years ago)

This is pretty intrusive.

Here we refactor the console initalization in noit_conf_checks to have
the reusable parts pulled into the noit_conf directly so that stratcon
can make use of them. noit_conf_checks is retooled to extend that and
override the 'ls' command to have it's old form and function.

stratcon_jlog_streamer extends the noit_conf console syntax to allow
adding and removing of noits within the 'configure terminal' mode.

# conf t
# noit 10.1.2.3
# noit 10.1.5.5:12345
# no noit 10.1.2.3
# no noit 10.1.5.5:12345

This should not change the operation of noitd's console in any way;
however, the changes were quite substantial, so we should keep an eye
out for collateral breakage.

refs #195

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