root/src/noit_module.c

Revision f796c4755663892ec87350c86b3d3786bc7b9436, 18.9 kB (checked in by Philip Maddox <pmaddox@circonus.com>, 8 months ago)

Allow "include" Path In Module Config

Extended the code that handles module configuration to allow an
"include" element above the "config" element. This will allow
include snippet XML files for module configuration.

  • 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 <dlfcn.h>
37 #include <assert.h>
38
39 #include <libxml/parser.h>
40 #include <libxslt/xslt.h>
41 #include <libxslt/xsltInternals.h>
42 #include <libxslt/transform.h>
43
44 #include "noit_module.h"
45 #include "noit_conf.h"
46 #include "utils/noit_hash.h"
47 #include "utils/noit_log.h"
48
49 NOIT_HOOK_IMPL(module_post_init,
50   (),
51   void *, closure,
52   (void *closure),
53   (closure))
54
55 static noit_module_t *
56 noit_load_module_image(noit_module_loader_t *loader,
57                        char *module_name,
58                        noit_conf_section_t section);
59
60 noit_module_loader_t __noit_image_loader = {
61   {
62     NOIT_LOADER_MAGIC,
63     NOIT_LOADER_ABI_VERSION,
64     "image",
65     "Basic binary image loader",
66     NULL
67   },
68   NULL,
69   NULL,
70   noit_load_module_image
71 };
72 struct __extended_image_data {
73   void *userdata;
74   void *dlhandle;
75 };
76
77 static noit_hash_table loaders = NOIT_HASH_EMPTY;
78 static noit_hash_table modules = NOIT_HASH_EMPTY;
79 static noit_hash_table generics = NOIT_HASH_EMPTY;
80 static int noit_module_load_failure_count = 0;
81
82 int noit_module_load_failures() {
83   return noit_module_load_failure_count;
84 }
85 noit_module_loader_t * noit_loader_lookup(const char *name) {
86   void *vloader;
87
88   if(noit_hash_retrieve(&loaders, name, strlen(name), &vloader))
89     return (noit_module_loader_t *)vloader;
90   return NULL;
91 }
92
93 static int
94 noit_module_list(noit_hash_table *t, const char ***f) {
95   noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
96   const char *name;
97   int klen, i = 0;
98   void *vhdr;
99
100   if(t->size == 0) {
101     *f = NULL;
102     return 0;
103   }
104
105   *f = calloc(t->size, sizeof(**f));
106   while(noit_hash_next(t, &iter, (const char **)&name, &klen,
107                        &vhdr)) {
108     noit_image_t *hdr = (noit_image_t *)vhdr;
109     (*f)[i++] = hdr->name;
110   }
111   return i;
112 }
113
114 int
115 noit_module_list_loaders(const char ***f) {
116   return noit_module_list(&loaders, f);
117 }
118
119 int
120 noit_module_list_modules(const char ***f) {
121   return noit_module_list(&modules, f);
122 }
123
124 int
125 noit_module_list_generics(const char ***f) {
126   return noit_module_list(&generics, f);
127 }
128
129 noit_module_t * noit_module_lookup(const char *name) {
130   void *vmodule;
131
132   if(noit_hash_retrieve(&modules, name, strlen(name), &vmodule))
133     return (noit_module_t *)vmodule;
134   return NULL;
135 }
136
137 noit_module_generic_t * noit_module_generic_lookup(const char *name) {
138   void *vmodule;
139
140   if(noit_hash_retrieve(&generics, name, strlen(name), &vmodule))
141     return (noit_module_generic_t *)vmodule;
142   return NULL;
143 }
144
145 static int noit_module_validate_magic(noit_image_t *obj) {
146   if (NOIT_IMAGE_MAGIC(obj) != NOIT_MODULE_MAGIC) return -1;
147   if (NOIT_IMAGE_VERSION(obj) != NOIT_MODULE_ABI_VERSION) return -1;
148   return 0;
149 }
150
151 static int noit_module_generic_validate_magic(noit_image_t *obj) {
152   if (NOIT_IMAGE_MAGIC(obj) != NOIT_GENERIC_MAGIC) return -1;
153   if (NOIT_IMAGE_VERSION(obj) != NOIT_GENERIC_ABI_VERSION) return -1;
154   return 0;
155 }
156
157 static int noit_module_loader_validate_magic(noit_image_t *obj) {
158   if (NOIT_IMAGE_MAGIC(obj) != NOIT_LOADER_MAGIC) return -1;
159   if (NOIT_IMAGE_VERSION(obj) != NOIT_LOADER_ABI_VERSION) return -1;
160   return 0;
161 }
162
163 noit_module_t *noit_blank_module() {
164   noit_module_t *obj;
165   obj = calloc(1, sizeof(*obj));
166   obj->hdr.opaque_handle = calloc(1, sizeof(struct __extended_image_data));
167   return obj;
168 }
169
170 int noit_register_module(noit_module_t *mod) {
171   return !noit_hash_store(&modules, mod->hdr.name, strlen(mod->hdr.name), mod);
172 }
173
174 int noit_load_image(const char *file, const char *name,
175                     noit_hash_table *registry,
176                     int (*validate)(noit_image_t *),
177                     size_t obj_size) {
178   char module_file[PATH_MAX];
179   char *base;
180   void *dlhandle;
181   void *dlsymbol;
182   noit_image_t *obj;
183
184   if(!noit_conf_get_string(NULL, "//modules/@directory", &base))
185     base = strdup("");
186
187   if(file[0] == '/')
188     strlcpy(module_file, file, sizeof(module_file));
189   else
190     snprintf(module_file, sizeof(module_file), "%s/%s.%s",
191              base, file, MODULEEXT);
192   free(base);
193
194   dlhandle = dlopen(module_file, RTLD_LAZY | RTLD_GLOBAL);
195   if(!dlhandle) {
196     noitL(noit_stderr, "Cannot open image '%s': %s\n",
197           module_file, dlerror());
198     return -1;
199   }
200
201   dlsymbol = dlsym(dlhandle, name);
202   if(!dlsymbol) {
203     noitL(noit_stderr, "Cannot find '%s' in image '%s': %s\n",
204           name, module_file, dlerror());
205     dlclose(dlhandle);
206     return -1;
207   }
208
209   if(validate(dlsymbol) == -1) {
210     noitL(noit_stderr, "I can't understand image %s\n", name);
211     dlclose(dlhandle);
212     return -1;
213   }
214
215   obj = calloc(1, obj_size);
216   memcpy(obj, dlsymbol, obj_size);
217   obj->opaque_handle = calloc(1, sizeof(struct __extended_image_data));
218
219   if(obj->onload && obj->onload(obj)) {
220     free(obj->opaque_handle);
221     free(obj);
222     dlclose(dlhandle);
223     return -1;
224   }
225   if(!noit_hash_store(registry, obj->name, strlen(obj->name), obj)) {
226     noitL(noit_error, "Attempted to load module %s more than once.\n", obj->name);
227     dlclose(dlhandle);
228     return -1;
229   }
230   ((struct __extended_image_data *)obj->opaque_handle)->dlhandle = dlhandle;
231   return 0;
232 }
233
234 static noit_module_generic_t *
235 noit_load_generic_image(noit_module_loader_t *loader,
236                         char *g_name,
237                         noit_conf_section_t section) {
238   char g_file[PATH_MAX];
239
240   if(!noit_conf_get_stringbuf(section, "ancestor-or-self::node()/@image",
241                               g_file, sizeof(g_file))) {
242     noitL(noit_stderr, "No image defined for %s\n", g_name);
243     return NULL;
244   }
245   if(noit_load_image(g_file, g_name, &generics,
246                      noit_module_generic_validate_magic,
247                      sizeof(noit_module_generic_t))) {
248     noitL(noit_stderr, "Could not load generic %s:%s\n", g_file, g_name);
249     return NULL;
250   }
251   return noit_module_generic_lookup(g_name);
252 }
253
254 static noit_module_loader_t *
255 noit_load_loader_image(noit_module_loader_t *loader,
256                        char *loader_name,
257                        noit_conf_section_t section) {
258   char loader_file[PATH_MAX];
259
260   if(!noit_conf_get_stringbuf(section, "ancestor-or-self::node()/@image",
261                               loader_file, sizeof(loader_file))) {
262     noitL(noit_stderr, "No image defined for %s\n", loader_name);
263     return NULL;
264   }
265   if(noit_load_image(loader_file, loader_name, &loaders,
266                      noit_module_loader_validate_magic,
267                      sizeof(noit_module_loader_t))) {
268     noitL(noit_stderr, "Could not load loader %s:%s\n", loader_file, loader_name);
269     noit_module_load_failure_count++;
270     return NULL;
271   }
272   return noit_loader_lookup(loader_name);
273 }
274
275 static noit_module_t *
276 noit_load_module_image(noit_module_loader_t *loader,
277                        char *module_name,
278                        noit_conf_section_t section) {
279   char module_file[PATH_MAX];
280
281   if(!noit_conf_get_stringbuf(section, "ancestor-or-self::node()/@image",
282                               module_file, sizeof(module_file))) {
283     noitL(noit_stderr, "No image defined for %s\n", module_name);
284     return NULL;
285   }
286   if(noit_load_image(module_file, module_name, &modules,
287                      noit_module_validate_magic, sizeof(noit_module_t))) {
288     noitL(noit_stderr, "Could not load module %s:%s\n", module_file, module_name);
289     return NULL;
290   }
291   return noit_module_lookup(module_name);
292 }
293
294 #include "module-online.h"
295
296 void noit_module_print_help(noit_console_closure_t ncct,
297                             noit_module_t *module, int examples) {
298   const char *params[3] = { NULL };
299   xmlDocPtr help = NULL, output = NULL;
300   xmlOutputBufferPtr out;
301   xmlCharEncodingHandlerPtr enc;
302   static xmlDocPtr helpStyleDoc = NULL;
303   static xsltStylesheetPtr helpStyle = NULL;
304   if(!helpStyle) {
305     if(!helpStyleDoc)
306       helpStyleDoc = xmlParseMemory(helpStyleXML, strlen(helpStyleXML));
307     if(!helpStyleDoc) {
308       nc_printf(ncct, "Invalid XML for style XML\n");
309       return;
310     }
311     helpStyle = xsltParseStylesheetDoc(helpStyleDoc);
312   }
313   if(!helpStyle) {
314     nc_printf(ncct, "no available stylesheet.\n");
315     return;
316   }
317   if(!module) {
318     nc_printf(ncct, "no module\n");
319     return;
320   }
321   if(!module->hdr.xml_description) {
322     nc_printf(ncct, "%s is undocumented, complain to the vendor.\n",
323               module->hdr.name);
324     return;
325   }
326   help = xmlParseMemory(module->hdr.xml_description,
327                         strlen(module->hdr.xml_description));
328   if(!help) {
329     nc_printf(ncct, "%s module has invalid XML documentation.\n",
330               module->hdr.name);
331     return;
332   }
333
334   if(examples) {
335     params[0] = "example";
336     params[1] = "'1'";
337     params[2] = NULL;
338   }
339   output = xsltApplyStylesheet(helpStyle, help, params);
340   if(!output) {
341     nc_printf(ncct, "formatting failed.\n");
342     goto out;
343   }
344
345   enc = xmlGetCharEncodingHandler(XML_CHAR_ENCODING_UTF8);
346   out = xmlOutputBufferCreateIO(noit_console_write_xml,
347                                 noit_console_close_xml,
348                                 ncct, enc);
349   xmlSaveFormatFileTo(out, output, "utf8", 1);
350
351  out:
352   if(help) xmlFreeDoc(help);
353   if(output) xmlFreeDoc(output);
354 }
355
356 char *
357 noit_module_options(noit_console_closure_t ncct,
358                     noit_console_state_stack_t *stack,
359                     noit_console_state_t *state,
360                     int argc, char **argv, int idx) {
361   if(argc == 1) {
362     /* List modules */
363     noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
364     const char *name;
365     int klen, i = 0;
366     void *vhdr;
367
368     while(noit_hash_next(&loaders, &iter, (const char **)&name, &klen,
369                          &vhdr)) {
370       noit_image_t *hdr = (noit_image_t *)vhdr;
371       if(!strncmp(hdr->name, argv[0], strlen(argv[0]))) {
372         if(idx == i) return strdup(hdr->name);
373         i++;
374       }
375     }
376     memset(&iter, 0, sizeof(iter));
377     while(noit_hash_next(&modules, &iter, (const char **)&name, &klen,
378                          &vhdr)) {
379       noit_image_t *hdr = (noit_image_t *)vhdr;
380       if(!strncmp(hdr->name, argv[0], strlen(argv[0]))) {
381         if(idx == i) return strdup(hdr->name);
382         i++;
383       }
384     }
385     memset(&iter, 0, sizeof(iter));
386     while(noit_hash_next(&generics, &iter, (const char **)&name, &klen,
387                          &vhdr)) {
388       noit_image_t *hdr = (noit_image_t *)vhdr;
389       if(!strncmp(hdr->name, argv[0], strlen(argv[0]))) {
390         if(idx == i) return strdup(hdr->name);
391         i++;
392       }
393     }
394     return NULL;
395   }
396   if(argc == 2) {
397     if(!strncmp("examples", argv[1], strlen(argv[1])))
398       if(idx == 0) return strdup("examples");
399   }
400   return NULL;
401 }
402 int
403 noit_module_help(noit_console_closure_t ncct,
404                  int argc, char **argv,
405                  noit_console_state_t *state, void *closure) {
406   if(argc == 0) {
407     /* List modules */
408     noit_hash_iter iter = NOIT_HASH_ITER_ZERO;
409     const char *name;
410     int klen;
411     void *vhdr;
412
413     nc_printf(ncct, "= Loaders, Modules, and Generics =\n");
414     while(noit_hash_next(&loaders, &iter, (const char **)&name, &klen,
415                          &vhdr)) {
416       noit_image_t *hdr = (noit_image_t *)vhdr;;
417       nc_printf(ncct, "  %s\n", hdr->name);
418     }
419     memset(&iter, 0, sizeof(iter));
420     while(noit_hash_next(&modules, &iter, (const char **)&name, &klen,
421                          &vhdr)) {
422       noit_image_t *hdr = (noit_image_t *)vhdr;;
423       nc_printf(ncct, "  %s\n", hdr->name);
424     }
425     memset(&iter, 0, sizeof(iter));
426     while(noit_hash_next(&generics, &iter, (const char **)&name, &klen,
427                          &vhdr)) {
428       noit_image_t *hdr = (noit_image_t *)vhdr;;
429       nc_printf(ncct, "  %s\n", hdr->name);
430     }
431     return 0;
432   }
433   else if(argc == 1 ||
434           (argc == 2 && !strcmp(argv[1], "examples"))) {
435     /* help for a specific module */
436     noit_module_t *mod;
437     mod = noit_module_lookup(argv[0]);
438     if(!mod) mod = (noit_module_t *)noit_module_generic_lookup(argv[0]);
439     noit_module_print_help(ncct, mod, argc == 2);
440     return 0;
441   }
442   nc_printf(ncct, "help module [ <modulename> [ examples ] ]\n");
443   return -1;
444 }
445
446 void noit_module_init() {
447   noit_conf_section_t *sections;
448   int i, cnt = 0;
449
450   noit_console_add_help("module", noit_module_help, noit_module_options);
451
452   /* Load our generic modules */
453   sections = noit_conf_get_sections(NULL, "//modules//generic", &cnt);
454   for(i=0; i<cnt; i++) {
455     char g_name[256];
456     noit_module_generic_t *gen;
457
458     if(!noit_conf_get_stringbuf(sections[i], "ancestor-or-self::node()/@name",
459                                 g_name, sizeof(g_name))) {
460       noitL(noit_stderr, "No name defined in generic stanza %d\n", i+1);
461       continue;
462     }
463     gen = noit_load_generic_image(&__noit_image_loader, g_name,
464                                   sections[i]);
465     if(!gen) {
466       noitL(noit_stderr, "Failed to load generic %s\n", g_name);
467       noit_module_load_failure_count++;
468       continue;
469     }
470     if(gen->config) {
471       int rv;
472       noit_hash_table *config;
473       config = noit_conf_get_hash(sections[i], "config");
474       rv = gen->config(gen, config);
475       if(rv == 0) {
476         noit_hash_destroy(config, free, free);
477         free(config);
478       }
479       else if(rv < 0) {
480         noitL(noit_stderr, "Failed to config generic %s\n", g_name);
481         continue;
482       }
483     }
484     if(gen->init && gen->init(gen)) {
485       noitL(noit_stderr, "Failed to init generic %s\n", g_name);
486       noit_module_load_failure_count++;
487     }
488     else
489       noitL(noit_debug, "Generic module %s successfully loaded.\n", g_name);
490   }
491   if(sections) free(sections);
492   /* Load our module loaders */
493   sections = noit_conf_get_sections(NULL, "//modules//loader", &cnt);
494   for(i=0; i<cnt; i++) {
495     char loader_name[256];
496     noit_module_loader_t *loader;
497
498     if(!noit_conf_get_stringbuf(sections[i], "ancestor-or-self::node()/@name",
499                                 loader_name, sizeof(loader_name))) {
500       noitL(noit_stderr, "No name defined in loader stanza %d\n", i+1);
501       continue;
502     }
503     loader = noit_load_loader_image(&__noit_image_loader, loader_name,
504                                     sections[i]);
505     if(!loader) {
506       noitL(noit_stderr, "Failed to load loader %s\n", loader_name);
507       noit_module_load_failure_count++;
508       continue;
509     }
510     if(loader->config) {
511       int rv;
512       noit_hash_table *config;
513       config = noit_conf_get_hash(sections[i], "config");
514       rv = loader->config(loader, config);
515       if(rv == 0) {
516         noit_hash_destroy(config, free, free);
517         free(config);
518       }
519       else if(rv < 0) {
520         noitL(noit_stderr, "Failed to config loader %s\n", loader_name);
521         noit_module_load_failure_count++;
522         continue;
523       }
524     }
525     if(loader->init && loader->init(loader))
526       noitL(noit_stderr, "Failed to init loader %s\n", loader_name);
527   }
528   if(sections) free(sections);
529
530   /* Load the modules (these *are* specific to the /noit/ root) */
531   sections = noit_conf_get_sections(NULL, "/noit/modules//module", &cnt);
532   if(!sections) cnt = 0;
533   for(i=0; i<cnt; i++) {
534     noit_module_loader_t *loader = &__noit_image_loader;
535     noit_hash_table *config;
536     noit_module_t *module;
537     noit_conf_section_t *include_sections;
538     char loader_name[256];
539     char module_name[256];
540     int section_cnt;
541
542     /* If no loader is specified, we should use the image loader */
543     if(!noit_conf_get_stringbuf(sections[i], "ancestor-or-self::node()/@name",
544                                 module_name, sizeof(module_name))) {
545       noitL(noit_stderr, "No name defined in module stanza %d\n", i+1);
546       continue;
547     }
548
549     if(noit_conf_get_stringbuf(sections[i], "ancestor-or-self::node()/@loader",
550                                 loader_name, sizeof(loader_name))) {
551       loader = noit_loader_lookup(loader_name);
552       if(!loader) {
553         noitL(noit_stderr, "No '%s' loader found.\n", loader_name);
554         noit_module_load_failure_count++;
555         continue;
556       }
557     } else {
558       strlcpy(loader_name, "image", sizeof(loader_name));
559     }
560
561     module = loader->load(loader, module_name, sections[i]);
562     if(!module) {
563       noitL(noit_stderr, "Loader '%s' failed to load '%s'.\n",
564             loader_name, module_name);
565       noit_module_load_failure_count++;
566       continue;
567     }
568     if(module->config) {
569       int rv;
570       include_sections = noit_conf_get_sections(sections[i], "include", &section_cnt);
571       if ((include_sections) && (section_cnt == 1)) {
572         config = noit_conf_get_hash(*include_sections, "config");
573       }
574       else {
575         config = noit_conf_get_hash(sections[i], "config");
576       }
577       rv = module->config(module, config);
578       if(rv == 0) {
579         /* Not an error,
580          * but the module didn't take responsibility for the config.
581          */
582         noit_hash_destroy(config, free, free);
583         free(config);
584       }
585       else if(rv < 0) {
586         noitL(noit_stderr,
587               "Configure failed on %s\n", module_name);
588         continue;
589       }
590       if(include_sections) free(include_sections);
591     }
592     if(module->init && module->init(module)) {
593       noitL(noit_stderr,
594             "Initialized failed on %s\n", module_name);
595       continue;
596     }
597     noitL(noit_debug, "Module %s successfully loaded.\n", module_name);
598   }
599   if(cnt) free(sections);
600
601   if(module_post_init_hook_invoke() == NOIT_HOOK_ABORT) {
602     noitL(noit_stderr, "Module post initialization phase failed.\n");
603     noit_module_load_failure_count++;
604   }
605 }
606
607 #define userdata_accessors(type, field) \
608 void *noit_##type##_get_userdata(noit_##type##_t *mod) { \
609   return mod->field->userdata; \
610 } \
611 void noit_##type##_set_userdata(noit_##type##_t *mod, void *newdata) { \
612   mod->field->userdata = newdata; \
613 }
614
615 userdata_accessors(image, opaque_handle)
616 userdata_accessors(module_loader, hdr.opaque_handle)
617 userdata_accessors(module, hdr.opaque_handle)
Note: See TracBrowser for help on using the browser.