root/src/noit_conf.c

Revision 01dbb0d0a665cdb6625e6290da3405ee89ddc80b, 16.4 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 7 years ago)

various valgrind noted leaks

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2007, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  */
5
6 #include "noit_defines.h"
7
8 #include <stdio.h>
9 #include <unistd.h>
10 #include <fcntl.h>
11 #include <errno.h>
12 #include <assert.h>
13 #include <libxml/parser.h>
14 #include <libxml/tree.h>
15 #include <libxml/xpath.h>
16
17 #include "noit_conf.h"
18 #include "noit_check.h"
19 #include "noit_console.h"
20 #include "utils/noit_hash.h"
21 #include "utils/noit_log.h"
22
23 /* tmp hash impl, replace this with something nice */
24 static noit_hash_table _tmp_config = NOIT_HASH_EMPTY;
25 static xmlDocPtr master_config = NULL;
26 static char master_config_file[PATH_MAX] = "";
27 static xmlXPathContextPtr xpath_ctxt = NULL;
28
29 static noit_hash_table _compiled_fallback = NOIT_HASH_EMPTY;
30 static struct {
31   const char *key;
32   const char *val;
33 } config_info[] = {
34   /*
35    * These are compile-time fallbacks to be used in the event
36    * that the current running config does not have values for
37    * these config paths.
38    *
39    * PLEASE: keep them alphabetically sorted.
40    */
41   { "/%s/eventer/@implementation", DEFAULT_EVENTER },
42   { "/%s/modules/@directory", MODULES_DIR },
43
44   { NULL, NULL }
45 };
46
47 void noit_conf_xml_error_func(void *ctx, const char *format, ...) {
48   struct timeval __now;
49   noit_log_stream_t ls = ctx;
50   va_list arg;
51   if(!ls) return;
52   va_start(arg, format);
53   gettimeofday(&__now,  NULL);
54   noit_vlog(ls, &__now, __FILE__, __LINE__, format, arg);
55   va_end(arg);
56 }
57 void noit_conf_xml_error_ext_func(void *ctx, xmlErrorPtr err) {
58   struct timeval __now;
59   noit_log_stream_t ls = ctx;
60   if(!ls) return;
61   gettimeofday(&__now,  NULL);
62   if(err->file)
63     noit_log(ls, &__now, err->file, err->line,
64              "XML error [%d/%d] in %s on line %d %s\n",
65              err->domain, err->code, err->file, err->line, err->message);
66   else
67     noit_log(ls, &__now, err->file, err->line,
68              "XML error [%d/%d] %s\n",
69              err->domain, err->code, err->message);
70 }
71 void noit_conf_init(const char *toplevel) {
72   int i;
73   char keystr[256];
74   xmlSetGenericErrorFunc(noit_error, noit_conf_xml_error_func);
75   xmlSetStructuredErrorFunc(noit_error, noit_conf_xml_error_ext_func);
76   for(i = 0; config_info[i].key != NULL; i++) {
77     snprintf(keystr, sizeof(keystr), config_info[i].key, toplevel);
78     noit_hash_store(&_compiled_fallback,
79                     strdup(keystr), strlen(keystr),
80                     (void *)strdup(config_info[i].val));
81   }
82   xmlKeepBlanksDefault(0);
83   xmlInitParser();
84   xmlXPathInit();
85 }
86
87 int noit_conf_load(const char *path) {
88   xmlDocPtr new_config;
89   new_config = xmlParseFile(path);
90   if(new_config) {
91     if(master_config) xmlFreeDoc(master_config);
92     if(xpath_ctxt) xmlXPathFreeContext(xpath_ctxt);
93
94     master_config = new_config;
95     xpath_ctxt = xmlXPathNewContext(master_config);
96     if(path != master_config_file) realpath(path, master_config_file);
97     return 0;
98   }
99   return -1;
100 }
101 int noit_conf_xml_xpath(xmlDocPtr *mc, xmlXPathContextPtr *xp) {
102   if(mc) *mc = master_config;
103   if(xp) *xp = xpath_ctxt;
104   return 0;
105 }
106 int noit_conf_save(const char *path) {
107   return -1;
108 }
109
110 void noit_conf_get_elements_into_hash(noit_conf_section_t section,
111                                       const char *path,
112                                       noit_hash_table *table) {
113   int i, cnt;
114   xmlXPathObjectPtr pobj = NULL;
115   xmlXPathContextPtr current_ctxt;
116   xmlNodePtr current_node = (xmlNodePtr)section;
117   xmlNodePtr node;
118
119   current_ctxt = xpath_ctxt;
120   if(current_node) {
121     current_ctxt = xmlXPathNewContext(master_config);
122     current_ctxt->node = current_node;
123   }
124   pobj = xmlXPathEval((xmlChar *)path, current_ctxt);
125   if(!pobj) goto out;
126   if(pobj->type != XPATH_NODESET) goto out;
127   if(xmlXPathNodeSetIsEmpty(pobj->nodesetval)) goto out;
128   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
129   for(i=0; i<cnt; i++) {
130     char *value;
131     node = xmlXPathNodeSetItem(pobj->nodesetval, i);
132     value = (char *)xmlXPathCastNodeToString(node);
133     noit_hash_replace(table,
134                       strdup((char *)node->name), strlen((char *)node->name),
135                       strdup(value), free, free);
136     xmlFree(value);
137   }
138  out:
139   if(pobj) xmlXPathFreeObject(pobj);
140   if(current_ctxt && current_ctxt != xpath_ctxt)
141     xmlXPathFreeContext(current_ctxt);
142 }
143 void noit_conf_get_into_hash(noit_conf_section_t section,
144                              const char *path,
145                              noit_hash_table *table) {
146   int cnt;
147   xmlXPathObjectPtr pobj = NULL;
148   xmlXPathContextPtr current_ctxt;
149   xmlNodePtr current_node = (xmlNodePtr)section;
150   xmlNodePtr node, parent_node;
151   char xpath_expr[1024];
152   char *inheritid;
153
154   current_ctxt = xpath_ctxt;
155   if(current_node) {
156     current_ctxt = xmlXPathNewContext(master_config);
157     current_ctxt->node = current_node;
158   }
159   if(path[0] == '/')
160     strlcpy(xpath_expr, path, sizeof(xpath_expr));
161   else
162     snprintf(xpath_expr, sizeof(xpath_expr),
163              "ancestor-or-self::node()/%s", path);
164   pobj = xmlXPathEval((xmlChar *)xpath_expr, current_ctxt);
165   if(!pobj) goto out;
166   if(pobj->type != XPATH_NODESET) goto out;
167   if(xmlXPathNodeSetIsEmpty(pobj->nodesetval)) goto out;
168   cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
169   /* These are in the order of root to leaf
170    * We want to recurse... apply:
171    *   1. our parent's config
172    *   2. our "inherit" config if it exists.
173    *   3. our config.
174    */
175   node = xmlXPathNodeSetItem(pobj->nodesetval, cnt-1);
176   /* 1. */
177   if(cnt > 1) {
178     parent_node = xmlXPathNodeSetItem(pobj->nodesetval, cnt-2);
179     noit_conf_get_into_hash(parent_node, (const char *)node->name, table);
180   }
181   /* 2. */
182   inheritid = (char *)xmlGetProp(node, (xmlChar *)"inherit");
183   if(inheritid) {
184     snprintf(xpath_expr, sizeof(xpath_expr), "//*[@id=\"%s\"]", inheritid);
185     noit_conf_get_into_hash(NULL, xpath_expr, table);
186   }
187   /* 3. */
188   noit_conf_get_elements_into_hash(node, "*", table);
189
190  out:
191   if(pobj) xmlXPathFreeObject(pobj);
192   if(current_ctxt && current_ctxt != xpath_ctxt)
193     xmlXPathFreeContext(current_ctxt);
194 }
195 noit_hash_table *noit_conf_get_hash(noit_conf_section_t section,
196                                     const char *path) {
197   noit_hash_table *table = NULL;
198
199   table = calloc(1, sizeof(*table));
200   noit_conf_get_into_hash(section, path, table);
201   return table;
202 }
203 noit_conf_section_t noit_conf_get_section(noit_conf_section_t section,
204                                           const char *path) {
205   noit_conf_section_t subsection = NULL;
206   xmlXPathObjectPtr pobj = NULL;
207   xmlXPathContextPtr current_ctxt;
208   xmlNodePtr current_node = (xmlNodePtr)section;
209
210   current_ctxt = xpath_ctxt;
211   if(current_node) {
212     current_ctxt = xmlXPathNewContext(master_config);
213     current_ctxt->node = current_node;
214   }
215   pobj = xmlXPathEval((xmlChar *)path, current_ctxt);
216   if(!pobj) goto out;
217   if(pobj->type != XPATH_NODESET) goto out;
218   if(xmlXPathNodeSetIsEmpty(pobj->nodesetval)) goto out;
219   subsection = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
220  out:
221   if(pobj) xmlXPathFreeObject(pobj);
222   if(current_ctxt && current_ctxt != xpath_ctxt)
223     xmlXPathFreeContext(current_ctxt);
224   return subsection;
225 }
226 noit_conf_section_t *noit_conf_get_sections(noit_conf_section_t section,
227                                             const char *path,
228                                             int *cnt) {
229   int i;
230   noit_conf_section_t *sections = NULL;
231   xmlXPathObjectPtr pobj = NULL;
232   xmlXPathContextPtr current_ctxt;
233   xmlNodePtr current_node = (xmlNodePtr)section;
234
235   *cnt = 0;
236   current_ctxt = xpath_ctxt;
237   if(current_node) {
238     current_ctxt = xmlXPathNewContext(master_config);
239     current_ctxt->node = current_node;
240   }
241   pobj = xmlXPathEval((xmlChar *)path, current_ctxt);
242   if(!pobj) goto out;
243   if(pobj->type != XPATH_NODESET) goto out;
244   if(xmlXPathNodeSetIsEmpty(pobj->nodesetval)) goto out;
245   *cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
246   sections = calloc(*cnt, sizeof(*sections));
247   for(i=0; i<*cnt; i++)
248     sections[i] = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
249  out:
250   if(pobj) xmlXPathFreeObject(pobj);
251   if(current_ctxt && current_ctxt != xpath_ctxt)
252     xmlXPathFreeContext(current_ctxt);
253   return sections;
254 }
255 int _noit_conf_get_string(noit_conf_section_t section, xmlNodePtr *vnode,
256                           const char *path, char **value) {
257   char *str;
258   int i, rv = 1;
259   xmlXPathObjectPtr pobj = NULL;
260   xmlXPathContextPtr current_ctxt;
261   xmlNodePtr current_node = (xmlNodePtr)section;
262
263   current_ctxt = xpath_ctxt;
264   if(current_node) {
265     current_ctxt = xmlXPathNewContext(master_config);
266     current_ctxt->node = current_node;
267   }
268   pobj = xmlXPathEval((xmlChar *)path, current_ctxt);
269   if(pobj) {
270     xmlNodePtr node;
271     switch(pobj->type) {
272       case XPATH_NODESET:
273         if(xmlXPathNodeSetIsEmpty(pobj->nodesetval)) goto fallback;
274         i = xmlXPathNodeSetGetLength(pobj->nodesetval);
275         node = xmlXPathNodeSetItem(pobj->nodesetval, i-1);
276         if(vnode) *vnode = node;
277         *value = (char *)xmlXPathCastNodeToString(node);
278         break;
279       default:
280         *value = (char *)xmlXPathCastToString(pobj);
281     }
282     goto found;
283   }
284  fallback:
285   if(noit_hash_retrieve(&_compiled_fallback,
286                         path, strlen(path), (void **)&str)) {
287     *value = (char *)xmlStrdup((xmlChar *)str);
288     goto found;
289   }
290   rv = 0;
291  found:
292   if(pobj) xmlXPathFreeObject(pobj);
293   if(current_ctxt && current_ctxt != xpath_ctxt)
294     xmlXPathFreeContext(current_ctxt);
295   return rv;
296 }
297 int noit_conf_get_uuid(noit_conf_section_t section,
298                        const char *path, uuid_t out) {
299   char *str;
300   if(_noit_conf_get_string(section,NULL,path,&str)) {
301     if(uuid_parse(str, out) == 0) return 1;
302     return 0;
303   }
304   return 0;
305 }
306 int noit_conf_get_string(noit_conf_section_t section,
307                          const char *path, char **value) {
308   char *str;
309   if(_noit_conf_get_string(section,NULL,path,&str)) {
310     *value = strdup(str);
311     xmlFree(str);
312     return 1;
313   }
314   return 0;
315 }
316 int noit_conf_get_stringbuf(noit_conf_section_t section,
317                             const char *path, char *buf, int len) {
318   char *str;
319   if(_noit_conf_get_string(section,NULL,path,&str)) {
320     strlcpy(buf, str, len);
321     xmlFree(str);
322     return 1;
323   }
324   return 0;
325 }
326 int noit_conf_set_string(noit_conf_section_t section,
327                          const char *path, const char *value) {
328   noit_hash_replace(&_tmp_config,
329                     strdup(path), strlen(path), (void *)strdup(value),
330                     free, free);
331   return 1;
332 }
333 int noit_conf_get_int(noit_conf_section_t section,
334                       const char *path, int *value) {
335   char *str;
336   long longval;
337   if(_noit_conf_get_string(section,NULL,path,&str)) {
338     int base = 10;
339     if(str[0] == '0') {
340       if(str[1] == 'x') base = 16;
341       else base = 8;
342     }
343     longval = strtol(str, NULL, base);
344     xmlFree(str);
345     *value = (int)longval;
346     return 1;
347   }
348   return 0;
349 }
350 int noit_conf_set_int(noit_conf_section_t section,
351                       const char *path, int value) {
352   char buffer[32];
353   snprintf(buffer, 32, "%d", value);
354   return noit_conf_set_string(section,path,buffer);
355 }
356 int noit_conf_get_float(noit_conf_section_t section,
357                         const char *path, float *value) {
358   char *str;
359   if(_noit_conf_get_string(section,NULL,path,&str)) {
360     *value = atof(str);
361     xmlFree(str);
362     return 1;
363   }
364   return 0;
365 }
366 int noit_conf_set_float(noit_conf_section_t section,
367                         const char *path, float value) {
368   char buffer[32];
369   snprintf(buffer, 32, "%f", value);
370   return noit_conf_set_string(section,path,buffer);
371 }
372 int noit_conf_get_boolean(noit_conf_section_t section,
373                           const char *path, noit_conf_boolean *value) {
374   char *str;
375   if(_noit_conf_get_string(section,NULL,path,&str)) {
376     if(!strcasecmp(str, "true") ||
377        !strcasecmp(str, "on")) *value = noit_true;
378     else *value = noit_false;
379     xmlFree(str);
380     return 1;
381   }
382   return 0;
383 }
384 int noit_conf_set_boolean(noit_conf_section_t section,
385                           const char *path, noit_conf_boolean value) {
386   if(value == noit_true)
387     return noit_conf_set_string(section,path,"true");
388   return noit_conf_set_string(section,path,"false");
389 }
390
391 static int
392 noit_console_write_xml(void *vncct, const char *buffer, int len) {
393   noit_console_closure_t ncct = vncct;
394   return nc_write(ncct, buffer, len);
395 }
396 static int
397 noit_console_close_xml(void *vncct) {
398   return 0;
399 }
400
401 int
402 noit_conf_reload(noit_console_closure_t ncct,
403                  int argc, char **argv,
404                  noit_console_state_t *state, void *closure) {
405   if(noit_conf_load(master_config_file)) {
406     nc_printf(ncct, "error loading config\n");
407     return -1;
408   }
409   return 0;
410 }
411 int
412 noit_conf_write_terminal(noit_console_closure_t ncct,
413                          int argc, char **argv,
414                          noit_console_state_t *state, void *closure) {
415   xmlOutputBufferPtr out;
416   xmlCharEncodingHandlerPtr enc;
417   enc = xmlGetCharEncodingHandler(XML_CHAR_ENCODING_UTF8);
418   out = xmlOutputBufferCreateIO(noit_console_write_xml,
419                                 noit_console_close_xml,
420                                 ncct, enc);
421   xmlSaveFormatFileTo(out, master_config, "utf8", 1);
422   return 0;
423 }
424 int
425 noit_conf_write_file(noit_console_closure_t ncct,
426                      int argc, char **argv,
427                      noit_console_state_t *state, void *closure) {
428   int fd, len;
429   char master_file_tmp[PATH_MAX];
430   xmlOutputBufferPtr out;
431   xmlCharEncodingHandlerPtr enc;
432
433   snprintf(master_file_tmp, sizeof(master_file_tmp),
434            "%s.tmp", master_config_file);
435   unlink(master_file_tmp);
436   fd = open(master_file_tmp, O_CREAT|O_EXCL|O_WRONLY, 0640);
437   if(fd < 0) {
438     nc_printf(ncct, "Failed to open tmp file: %s\n", strerror(errno));
439     return -1;
440   }
441   enc = xmlGetCharEncodingHandler(XML_CHAR_ENCODING_UTF8);
442   out = xmlOutputBufferCreateFd(fd, enc);
443   if(!out) {
444     close(fd);
445     unlink(master_file_tmp);
446     nc_printf(ncct, "internal error: OutputBufferCreate failed\n");
447     return -1;
448   }
449   len = xmlSaveFormatFileTo(out, master_config, "utf8", 1);
450   close(fd);
451   if(len <= 0) {
452     nc_printf(ncct, "internal error: writing to tmp file failed.\n");
453     return -1;
454   }
455   if(rename(master_file_tmp, master_config_file) != 0) {
456     nc_printf(ncct, "Failed to replace file: %s\n", strerror(errno));
457     return -1;
458   }
459   nc_printf(ncct, "%d bytes written.\n", len);
460   return 0;
461 }
462
463 void
464 noit_conf_log_init(const char *toplevel) {
465   int i, cnt = 0, o, ocnt = 0;
466   noit_conf_section_t *log_configs, *outlets;
467   char path[256];
468
469   snprintf(path, sizeof(path), "/%s/logs//log", toplevel);
470   log_configs = noit_conf_get_sections(NULL, path, &cnt);
471   noitL(noit_stderr, "Found %d %s stanzas\n", cnt, path);
472   for(i=0; i<cnt; i++) {
473     noit_log_stream_t ls;
474     char name[256], type[256], path[256];
475     noit_hash_table *config;
476     noit_conf_boolean disabled;
477
478     if(!noit_conf_get_stringbuf(log_configs[i],
479                                 "ancestor-or-self::node()/@name",
480                                 name, sizeof(name))) {
481       noitL(noit_error, "log section %d does not have a name attribute\n", i+1);
482       exit(-1);
483     }
484     if(!noit_conf_get_stringbuf(log_configs[i],
485                                 "ancestor-or-self::node()/@type",
486                                 type, sizeof(type))) {
487       type[0] = '\0';
488     }
489     if(!noit_conf_get_stringbuf(log_configs[i],
490                                 "ancestor-or-self::node()/@path",
491                                 path, sizeof(path))) {
492       path[0] = '\0';
493     }
494     config = noit_conf_get_hash(log_configs[i],
495                                 "ancestor-or-self::node()/config/*");
496     ls = noit_log_stream_new(name, type[0] ? type : NULL,
497                              path[0] ? path : NULL, config);
498     if(!ls) {
499       fprintf(stderr, "Error configuring log: %s[%s:%s]\n", name, type, path);
500       exit(-1);
501     }
502
503     if(noit_conf_get_boolean(log_configs[i],
504                              "ancestor-or-self::node()/@disabled",
505                              &disabled) && disabled)
506       ls->enabled = 0;
507      
508     outlets = noit_conf_get_sections(log_configs[i],
509                                      "ancestor-or-self::node()/outlet", &ocnt);
510     noitL(noit_debug, "found %d outlets for log '%s'\n", ocnt, name);
511
512     for(o=0; o<ocnt; o++) {
513       noit_log_stream_t outlet;
514       char oname[256];
515       noit_conf_get_stringbuf(outlets[o], "@name",
516                               oname, sizeof(oname));
517       outlet = noit_log_stream_find(oname);
518       if(!outlet) {
519         fprintf(stderr, "Cannot find outlet '%s' for %s[%s:%s]\n", oname,
520               name, type, path);
521         exit(-1);
522       }
523       else
524         noit_log_stream_add_stream(ls, outlet);
525     }
526     if(outlets) free(outlets);
527   }
528   if(log_configs) free(log_configs);
529 }
Note: See TracBrowser for help on using the browser.