]> git.decadent.org.uk Git - ion3.git/blob - ioncore/modules.c
10e1c29dddacd3c4a82d66a706b11273acea8e56
[ion3.git] / ioncore / modules.c
1 /*
2  * ion/ioncore/modules.c
3  *
4  * Copyright (c) Tuomo Valkonen 1999-2008. 
5  *
6  * See the included file LICENSE for details.
7  */
8
9 #include <string.h>
10 #include <ctype.h>
11 #include <dlfcn.h>
12 #include <unistd.h>
13
14 #include <libtu/rb.h>
15 #include <libextl/readconfig.h>
16
17 #include "common.h"
18 #include "modules.h"
19 #include "../version.h"
20
21
22 #ifndef CF_PRELOAD_MODULES
23
24
25 /*{{{ Module list */
26
27
28 typedef void *dlhandle;
29
30
31 static Rb_node modules=NULL;
32
33
34 static dlhandle get_handle(const char *name)
35 {
36     int found=0;
37     Rb_node nd;
38     
39     nd=rb_find_key_n(modules, name, &found);
40     if(found)
41         return nd->v.val;
42     return NULL;
43 }
44
45
46 static const char *get_name(dlhandle handle)
47 {
48     Rb_node nd;
49     
50     rb_traverse(nd, modules){
51         if(nd->v.val==handle)
52             return (const char *)(nd->k.key);
53     }
54     return NULL;
55 }
56
57
58 static Rb_node add_module(char *name, dlhandle handle)
59 {
60     return rb_insert(modules, name, handle);
61 }
62
63
64 /*}}}*/
65
66
67 /*{{{ Module symbol access */
68
69
70 static void *get_module_symbol(dlhandle handle, 
71                                const char *modulename, 
72                                const char *name)
73 {
74     char *p;
75     void *ret;
76
77     p=scat(modulename, name);
78     
79     if(p==NULL)
80         return NULL;
81     
82     ret=dlsym(handle, p);
83     
84     free(p);
85     
86     return ret;
87 }
88
89 static bool check_version(dlhandle handle, const char *modulename)
90 {
91     char *versionstr=(char*)get_module_symbol(handle, modulename, 
92                                               "_ion_api_version");
93     if(versionstr==NULL)
94         return FALSE;
95     return (strcmp(versionstr, ION_API_VERSION)==0);
96 }
97
98
99 static bool call_init(dlhandle handle, const char *modulename)
100 {
101     bool (*initfn)(void);
102     
103     initfn=(bool (*)())get_module_symbol(handle, modulename, "_init");
104     
105     if(initfn==NULL)
106         return TRUE;
107     
108     return initfn();
109 }
110
111
112 static void call_deinit(dlhandle handle, const char *modulename)
113 {
114     void (*deinitfn)(void);
115     
116     deinitfn=(void (*)())get_module_symbol(handle, modulename, "_deinit");
117     
118     if(deinitfn!=NULL)
119         deinitfn();
120 }
121
122
123 /*}}}*/
124
125
126 /*{{{ Init */
127
128
129 bool ioncore_init_module_support()
130 {
131     modules=make_rb();
132     return (modules!=NULL);
133 }
134
135
136 static int try_load(const char *file, void *param)
137 {
138     dlhandle handle=NULL;
139     const char *slash, *dot;
140     char *name;
141     Rb_node mod;
142     
143     if(access(file, F_OK)!=0)
144         return EXTL_TRYCONFIG_NOTFOUND;
145
146     slash=strrchr(file, '/');
147     dot=strrchr(file, '.');
148     
149     if(slash==NULL)
150         slash=file;
151     else
152         slash++;
153     
154     if(dot<=slash){
155         warn(TR("Invalid module name."));
156         goto err1;
157     }
158     
159     name=ALLOC_N(char, dot-slash+1);
160     if(name==NULL)
161         goto err1;
162     
163     strncpy(name, slash, dot-slash);
164     name[dot-slash]='\0';
165     
166     if(get_handle(name)){
167         warn_obj(file, TR("The module is already loaded."));
168         goto err2;
169     }
170         
171     handle=dlopen(file, RTLD_NOW|RTLD_GLOBAL);
172
173     if(handle==NULL){
174         warn_obj(file, "%s", dlerror());
175         goto err2;
176     }
177     
178     if(get_name(handle))
179         return EXTL_TRYCONFIG_OK;
180     
181     if(!check_version(handle, name)){
182         warn_obj(file, TR("Module version information not found or "
183                           "version mismatch. Refusing to use."));
184         goto err3;
185     }
186     
187     mod=add_module(name, handle);
188     
189     if(mod==NULL)
190         goto err3;
191     
192     if(!call_init(handle, name)){
193         warn_obj(file, TR("Unable to initialise module %s."), name);
194         rb_delete_node(mod);
195         goto err3;
196     }
197     
198     return EXTL_TRYCONFIG_OK;
199
200 err3:    
201     dlclose(handle);
202 err2:
203     free(name);
204 err1:
205     return EXTL_TRYCONFIG_LOAD_FAILED;
206 }
207
208
209 static bool do_load_module(const char *modname)
210 {
211     int retval;
212     
213     retval=extl_try_config(modname, NULL, (ExtlTryConfigFn*)try_load, 
214                            NULL, "so", NULL);
215     
216     if(retval==EXTL_TRYCONFIG_NOTFOUND)
217         warn(TR("Unable to find '%s' on search path."), modname);
218     
219     return (retval==EXTL_TRYCONFIG_OK);
220 }
221
222
223 /*}}}*/
224
225
226 /*{{{ Deinit */
227
228
229 static void do_unload_module(Rb_node mod)
230 {
231     char *name=(char*)mod->k.key;
232     dlhandle handle=mod->v.val;
233     
234     call_deinit(handle, name);
235
236     dlclose(handle);
237     free(name);
238 }
239
240
241 void ioncore_unload_modules()
242 {
243     Rb_node mod;
244     
245     rb_traverse(mod, modules){
246         do_unload_module(mod);
247     }
248 }
249
250
251 /*}}}*/
252
253
254 #else
255
256
257 /*{{{ Static module support */
258
259
260 static bool call_init(WStaticModuleInfo *handle)
261 {
262     if(handle->init!=NULL)
263         return handle->init();
264     return TRUE;
265 }
266
267
268 static void call_deinit(WStaticModuleInfo *handle)
269 {
270     if(handle->deinit!=NULL)
271         handle->deinit();
272 }
273
274
275 extern WStaticModuleInfo ioncore_static_modules[];
276
277
278 static bool do_load_module(const char *name)
279 {
280     WStaticModuleInfo *mod;
281     
282     for(mod=ioncore_static_modules; mod->name!=NULL; mod++){
283         if(strcmp(mod->name, name)==0)
284             break;
285     }
286     
287     if(mod->name==NULL){
288         warn_obj(name, TR("Unknown module."));
289         return FALSE;
290     }
291     
292     if(mod->loaded)
293         return TRUE;
294
295     if(!call_init(mod)){
296         warn_obj(name, TR("Unable to initialise module."));
297         return FALSE;
298     }
299     
300     mod->loaded=TRUE;
301     
302     return TRUE;
303 }
304
305
306 void ioncore_unload_modules()
307 {
308     WStaticModuleInfo *mod;
309         
310     for(mod=ioncore_static_modules; mod->name!=NULL; mod++){
311         if(mod->loaded){
312             call_deinit(mod);
313             mod->loaded=FALSE;
314         }
315     }
316 }
317
318
319 bool ioncore_init_module_support()
320 {
321     return TRUE;
322 }
323
324
325 /*}}}*/
326
327
328 #endif
329
330
331 /*{{{ Exports */
332
333
334 /*EXTL_DOC
335  * Attempt to load a C-side module. 
336  */
337 EXTL_EXPORT
338 bool ioncore_load_module(const char *modname)
339 {
340     if(modname==NULL){
341         warn(TR("No module to load given."));
342         return FALSE;
343     }
344     return do_load_module(modname);
345 }
346
347
348 /*}}}*/
349