root/src/noit_module.c

Revision d2d82a9a53340a06eb8bf7c7a9d67dddc345ffef, 16.6 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 4 years ago)

fixes #239

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