root/src/noit_module.c

Revision 88a71780101cbf23034aa0cb840f9f0368fda2dd, 13.9 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 6 years ago)

fixes #126

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