root/src/noit_conf.c

Revision 2ccf5eb9e64a6e89f4c8e0022eee38cde7e74717, 16.5 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 7 years ago)

I think this finishes up #15. I'll not close just yet as I'm not sure how safe it is to reload all the checks without regard if their are in-flight or not. refs #51

  • 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     if(parent_node != current_node)
180       noit_conf_get_into_hash(parent_node, (const char *)node->name, table);
181   }
182   /* 2. */
183   inheritid = (char *)xmlGetProp(node, (xmlChar *)"inherit");
184   if(inheritid) {
185     snprintf(xpath_expr, sizeof(xpath_expr), "//*[@id=\"%s\"]", inheritid);
186     noit_conf_get_into_hash(NULL, xpath_expr, table);
187   }
188   /* 3. */
189   noit_conf_get_elements_into_hash(node, "*", table);
190
191  out:
192   if(pobj) xmlXPathFreeObject(pobj);
193   if(current_ctxt && current_ctxt != xpath_ctxt)
194     xmlXPathFreeContext(current_ctxt);
195 }
196 noit_hash_table *noit_conf_get_hash(noit_conf_section_t section,
197                                     const char *path) {
198   noit_hash_table *table = NULL;
199
200   table = calloc(1, sizeof(*table));
201   noit_conf_get_into_hash(section, path, table);
202   return table;
203 }
204 noit_conf_section_t noit_conf_get_section(noit_conf_section_t section,
205                                           const char *path) {
206   noit_conf_section_t subsection = NULL;
207   xmlXPathObjectPtr pobj = NULL;
208   xmlXPathContextPtr current_ctxt;
209   xmlNodePtr current_node = (xmlNodePtr)section;
210
211   current_ctxt = xpath_ctxt;
212   if(current_node) {
213     current_ctxt = xmlXPathNewContext(master_config);
214     current_ctxt->node = current_node;
215   }
216   pobj = xmlXPathEval((xmlChar *)path, current_ctxt);
217   if(!pobj) goto out;
218   if(pobj->type != XPATH_NODESET) goto out;
219   if(xmlXPathNodeSetIsEmpty(pobj->nodesetval)) goto out;
220   subsection = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, 0);
221  out:
222   if(pobj) xmlXPathFreeObject(pobj);
223   if(current_ctxt && current_ctxt != xpath_ctxt)
224     xmlXPathFreeContext(current_ctxt);
225   return subsection;
226 }
227 noit_conf_section_t *noit_conf_get_sections(noit_conf_section_t section,
228                                             const char *path,
229                                             int *cnt) {
230   int i;
231   noit_conf_section_t *sections = NULL;
232   xmlXPathObjectPtr pobj = NULL;
233   xmlXPathContextPtr current_ctxt;
234   xmlNodePtr current_node = (xmlNodePtr)section;
235
236   *cnt = 0;
237   current_ctxt = xpath_ctxt;
238   if(current_node) {
239     current_ctxt = xmlXPathNewContext(master_config);
240     current_ctxt->node = current_node;
241   }
242   pobj = xmlXPathEval((xmlChar *)path, current_ctxt);
243   if(!pobj) goto out;
244   if(pobj->type != XPATH_NODESET) goto out;
245   if(xmlXPathNodeSetIsEmpty(pobj->nodesetval)) goto out;
246   *cnt = xmlXPathNodeSetGetLength(pobj->nodesetval);
247   sections = calloc(*cnt, sizeof(*sections));
248   for(i=0; i<*cnt; i++)
249     sections[i] = (noit_conf_section_t)xmlXPathNodeSetItem(pobj->nodesetval, i);
250  out:
251   if(pobj) xmlXPathFreeObject(pobj);
252   if(current_ctxt && current_ctxt != xpath_ctxt)
253     xmlXPathFreeContext(current_ctxt);
254   return sections;
255 }
256 int _noit_conf_get_string(noit_conf_section_t section, xmlNodePtr *vnode,
257                           const char *path, char **value) {
258   char *str;
259   int i, rv = 1;
260   xmlXPathObjectPtr pobj = NULL;
261   xmlXPathContextPtr current_ctxt;
262   xmlNodePtr current_node = (xmlNodePtr)section;
263
264   current_ctxt = xpath_ctxt;
265   if(current_node) {
266     current_ctxt = xmlXPathNewContext(master_config);
267     current_ctxt->node = current_node;
268   }
269   pobj = xmlXPathEval((xmlChar *)path, current_ctxt);
270   if(pobj) {
271     xmlNodePtr node;
272     switch(pobj->type) {
273       case XPATH_NODESET:
274         if(xmlXPathNodeSetIsEmpty(pobj->nodesetval)) goto fallback;
275         i = xmlXPathNodeSetGetLength(pobj->nodesetval);
276         node = xmlXPathNodeSetItem(pobj->nodesetval, i-1);
277         if(vnode) *vnode = node;
278         *value = (char *)xmlXPathCastNodeToString(node);
279         break;
280       default:
281         *value = (char *)xmlXPathCastToString(pobj);
282     }
283     goto found;
284   }
285  fallback:
286   if(noit_hash_retrieve(&_compiled_fallback,
287                         path, strlen(path), (void **)&str)) {
288     *value = (char *)xmlStrdup((xmlChar *)str);
289     goto found;
290   }
291   rv = 0;
292  found:
293   if(pobj) xmlXPathFreeObject(pobj);
294   if(current_ctxt && current_ctxt != xpath_ctxt)
295     xmlXPathFreeContext(current_ctxt);
296   return rv;
297 }
298 int noit_conf_get_uuid(noit_conf_section_t section,
299                        const char *path, uuid_t out) {
300   char *str;
301   if(_noit_conf_get_string(section,NULL,path,&str)) {
302     if(uuid_parse(str, out) == 0) return 1;
303     return 0;
304   }
305   return 0;
306 }
307 int noit_conf_get_string(noit_conf_section_t section,
308                          const char *path, char **value) {
309   char *str;
310   if(_noit_conf_get_string(section,NULL,path,&str)) {
311     *value = strdup(str);
312     xmlFree(str);
313     return 1;
314   }
315   return 0;
316 }
317 int noit_conf_get_stringbuf(noit_conf_section_t section,
318                             const char *path, char *buf, int len) {
319   char *str;
320   if(_noit_conf_get_string(section,NULL,path,&str)) {
321     strlcpy(buf, str, len);
322     xmlFree(str);
323     return 1;
324   }
325   return 0;
326 }
327 int noit_conf_set_string(noit_conf_section_t section,
328                          const char *path, const char *value) {
329   noit_hash_replace(&_tmp_config,
330                     strdup(path), strlen(path), (void *)strdup(value),
331                     free, free);
332   return 1;
333 }
334 int noit_conf_get_int(noit_conf_section_t section,
335                       const char *path, int *value) {
336   char *str;
337   long longval;
338   if(_noit_conf_get_string(section,NULL,path,&str)) {
339     int base = 10;
340     if(str[0] == '0') {
341       if(str[1] == 'x') base = 16;
342       else base = 8;
343     }
344     longval = strtol(str, NULL, base);
345     xmlFree(str);
346     *value = (int)longval;
347     return 1;
348   }
349   return 0;
350 }
351 int noit_conf_set_int(noit_conf_section_t section,
352                       const char *path, int value) {
353   char buffer[32];
354   snprintf(buffer, 32, "%d", value);
355   return noit_conf_set_string(section,path,buffer);
356 }
357 int noit_conf_get_float(noit_conf_section_t section,
358                         const char *path, float *value) {
359   char *str;
360   if(_noit_conf_get_string(section,NULL,path,&str)) {
361     *value = atof(str);
362     xmlFree(str);
363     return 1;
364   }
365   return 0;
366 }
367 int noit_conf_set_float(noit_conf_section_t section,
368                         const char *path, float value) {
369   char buffer[32];
370   snprintf(buffer, 32, "%f", value);
371   return noit_conf_set_string(section,path,buffer);
372 }
373 int noit_conf_get_boolean(noit_conf_section_t section,
374                           const char *path, noit_conf_boolean *value) {
375   char *str;
376   if(_noit_conf_get_string(section,NULL,path,&str)) {
377     if(!strcasecmp(str, "true") ||
378        !strcasecmp(str, "on")) *value = noit_true;
379     else *value = noit_false;
380     xmlFree(str);
381     return 1;
382   }
383   return 0;
384 }
385 int noit_conf_set_boolean(noit_conf_section_t section,
386                           const char *path, noit_conf_boolean value) {
387   if(value == noit_true)
388     return noit_conf_set_string(section,path,"true");
389   return noit_conf_set_string(section,path,"false");
390 }
391
392 static int
393 noit_console_write_xml(void *vncct, const char *buffer, int len) {
394   noit_console_closure_t ncct = vncct;
395   return nc_write(ncct, buffer, len);
396 }
397 static int
398 noit_console_close_xml(void *vncct) {
399   return 0;
400 }
401
402 int
403 noit_conf_reload(noit_console_closure_t ncct,
404                  int argc, char **argv,
405                  noit_console_state_t *state, void *closure) {
406   if(noit_conf_load(master_config_file)) {
407     nc_printf(ncct, "error loading config\n");
408     return -1;
409   }
410   return 0;
411 }
412 int
413 noit_conf_write_terminal(noit_console_closure_t ncct,
414                          int argc, char **argv,
415                          noit_console_state_t *state, void *closure) {
416   xmlOutputBufferPtr out;
417   xmlCharEncodingHandlerPtr enc;
418   enc = xmlGetCharEncodingHandler(XML_CHAR_ENCODING_UTF8);
419   out = xmlOutputBufferCreateIO(noit_console_write_xml,
420                                 noit_console_close_xml,
421                                 ncct, enc);
422   xmlSaveFormatFileTo(out, master_config, "utf8", 1);
423   return 0;
424 }
425 int
426 noit_conf_write_file(noit_console_closure_t ncct,
427                      int argc, char **argv,
428                      noit_console_state_t *state, void *closure) {
429   int fd, len;
430   char master_file_tmp[PATH_MAX];
431   xmlOutputBufferPtr out;
432   xmlCharEncodingHandlerPtr enc;
433
434   snprintf(master_file_tmp, sizeof(master_file_tmp),
435            "%s.tmp", master_config_file);
436   unlink(master_file_tmp);
437   fd = open(master_file_tmp, O_CREAT|O_EXCL|O_WRONLY, 0640);
438   if(fd < 0) {
439     nc_printf(ncct, "Failed to open tmp file: %s\n", strerror(errno));
440     return -1;
441   }
442   enc = xmlGetCharEncodingHandler(XML_CHAR_ENCODING_UTF8);
443   out = xmlOutputBufferCreateFd(fd, enc);
444   if(!out) {
445     close(fd);
446     unlink(master_file_tmp);
447     nc_printf(ncct, "internal error: OutputBufferCreate failed\n");
448     return -1;
449   }
450   len = xmlSaveFormatFileTo(out, master_config, "utf8", 1);
451   close(fd);
452   if(len <= 0) {
453     nc_printf(ncct, "internal error: writing to tmp file failed.\n");
454     return -1;
455   }
456   if(rename(master_file_tmp, master_config_file) != 0) {
457     nc_printf(ncct, "Failed to replace file: %s\n", strerror(errno));
458     return -1;
459   }
460   nc_printf(ncct, "%d bytes written.\n", len);
461   return 0;
462 }
463
464 void
465 noit_conf_log_init(const char *toplevel) {
466   int i, cnt = 0, o, ocnt = 0;
467   noit_conf_section_t *log_configs, *outlets;
468   char path[256];
469
470   snprintf(path, sizeof(path), "/%s/logs//log", toplevel);
471   log_configs = noit_conf_get_sections(NULL, path, &cnt);
472   noitL(noit_stderr, "Found %d %s stanzas\n", cnt, path);
473   for(i=0; i<cnt; i++) {
474     noit_log_stream_t ls;
475     char name[256], type[256], path[256];
476     noit_hash_table *config;
477     noit_conf_boolean disabled;
478
479     if(!noit_conf_get_stringbuf(log_configs[i],
480                                 "ancestor-or-self::node()/@name",
481                                 name, sizeof(name))) {
482       noitL(noit_error, "log section %d does not have a name attribute\n", i+1);
483       exit(-1);
484     }
485     if(!noit_conf_get_stringbuf(log_configs[i],
486                                 "ancestor-or-self::node()/@type",
487                                 type, sizeof(type))) {
488       type[0] = '\0';
489     }
490     if(!noit_conf_get_stringbuf(log_configs[i],
491                                 "ancestor-or-self::node()/@path",
492                                 path, sizeof(path))) {
493       path[0] = '\0';
494     }
495     config = noit_conf_get_hash(log_configs[i],
496                                 "ancestor-or-self::node()/config/*");
497     ls = noit_log_stream_new(name, type[0] ? type : NULL,
498                              path[0] ? path : NULL, config);
499     if(!ls) {
500       fprintf(stderr, "Error configuring log: %s[%s:%s]\n", name, type, path);
501       exit(-1);
502     }
503
504     if(noit_conf_get_boolean(log_configs[i],
505                              "ancestor-or-self::node()/@disabled",
506                              &disabled) && disabled)
507       ls->enabled = 0;
508      
509     outlets = noit_conf_get_sections(log_configs[i],
510                                      "ancestor-or-self::node()/outlet", &ocnt);
511     noitL(noit_debug, "found %d outlets for log '%s'\n", ocnt, name);
512
513     for(o=0; o<ocnt; o++) {
514       noit_log_stream_t outlet;
515       char oname[256];
516       noit_conf_get_stringbuf(outlets[o], "@name",
517                               oname, sizeof(oname));
518       outlet = noit_log_stream_find(oname);
519       if(!outlet) {
520         fprintf(stderr, "Cannot find outlet '%s' for %s[%s:%s]\n", oname,
521               name, type, path);
522         exit(-1);
523       }
524       else
525         noit_log_stream_add_stream(ls, outlet);
526     }
527     if(outlets) free(outlets);
528   }
529   if(log_configs) free(log_configs);
530 }
Note: See TracBrowser for help on using the browser.