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