root/src/noit_module.c

Revision 56c7e9dd00981d9018a28e7adf1fb6144821c51e, 12.3 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 5 years ago)

refs #37... lots of progress here

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