Simple lua repl for testing
[notion.git] / ioncore / modules.c
blob8c8b8e28b3b62acb3acfb721fb8f6c336fe89cbc
1 /*
2 * ion/ioncore/modules.c
4 * Copyright (c) Arnout Engelen 2011
5 * Copyright (c) Tuomo Valkonen 1999-2009.
7 * See the included file LICENSE for details.
8 */
10 #include <string.h>
11 #include <ctype.h>
12 #include <dlfcn.h>
13 #include <unistd.h>
15 #include <libtu/rb.h>
16 #include <libextl/readconfig.h>
18 #include "common.h"
19 #include "modules.h"
20 #include "../version.h"
23 #ifndef CF_PRELOAD_MODULES
26 /*{{{ Module list */
29 typedef void *dlhandle;
32 static Rb_node modules=NULL;
35 static dlhandle get_handle(const char *name)
37 int found=0;
38 Rb_node nd;
40 nd=rb_find_key_n(modules, name, &found);
41 if(found)
42 return nd->v.val;
43 return NULL;
47 static const char *get_name(dlhandle handle)
49 Rb_node nd;
51 rb_traverse(nd, modules){
52 if(nd->v.val==handle)
53 return (const char *)(nd->k.key);
55 return NULL;
59 static Rb_node add_module(char *name, dlhandle handle)
61 return rb_insert(modules, name, handle);
65 /*}}}*/
68 /*{{{ Module symbol access */
71 static void *get_module_symbol(dlhandle handle,
72 const char *modulename,
73 const char *name)
75 char *p;
76 void *ret;
78 p=scat(modulename, name);
80 if(p==NULL)
81 return NULL;
83 ret=dlsym(handle, p);
85 free(p);
87 return ret;
90 static void (*get_module_fptr(dlhandle handle,
91 const char *modulename,
92 const char *name))(void **)
94 /* This is not valid ISO C. However, it is 'tried and tested'. The
95 * workaround originally recommended[1] is not alias-safe[2]. The approach
96 * we chose, while not valid ISO C, *is* valid under POSIX 2008[3]. Newer
97 * versions of GCC should not warn about it anymore[4].
99 * [1] http://pubs.opengroup.org/onlinepubs/009695399/functions/dlsym.html#tag_03_112_06)
100 * [2] http://gcc.gnu.org/bugzilla/show_bug.cgi?id=45289#c1
101 * [3] http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_12_03
102 * [4] http://gcc.gnu.org/bugzilla/show_bug.cgi?id=45289#c10
104 return (void (*)(void**)) get_module_symbol(handle, modulename, name);
107 static char *get_version(dlhandle handle, const char *modulename)
109 return (char*)get_module_symbol(handle, modulename,
110 "_ion_api_version");
113 static bool check_has_version(dlhandle handle, const char *modulename)
115 return get_version(handle, modulename) != NULL;
118 static bool check_version(dlhandle handle, const char *modulename)
120 char *versionstr=get_version(handle, modulename);
121 if(versionstr==NULL)
122 return FALSE;
123 return (strcmp(versionstr, NOTION_API_VERSION)==0);
127 static bool call_init(dlhandle handle, const char *modulename)
129 bool (*initfn)(void);
131 initfn=(bool (*)())get_module_fptr(handle, modulename, "_init");
133 if(initfn==NULL)
134 return TRUE;
136 return initfn();
140 static void call_deinit(dlhandle handle, const char *modulename)
142 void (*deinitfn)(void);
144 deinitfn=(void (*)())get_module_fptr(handle, modulename, "_deinit");
146 if(deinitfn!=NULL)
147 deinitfn();
151 /*}}}*/
154 /*{{{ Init */
157 bool ioncore_init_module_support()
159 modules=make_rb();
160 return (modules!=NULL);
164 static int try_load(const char *file, void *param)
166 dlhandle handle=NULL;
167 const char *slash, *dot;
168 char *name;
169 Rb_node mod;
171 if(access(file, F_OK)!=0)
172 return EXTL_TRYCONFIG_NOTFOUND;
174 slash=strrchr(file, '/');
175 dot=strrchr(file, '.');
177 if(slash==NULL)
178 slash=file;
179 else
180 slash++;
182 if(dot<=slash){
183 warn(TR("Invalid module name."));
184 goto err1;
187 name=ALLOC_N(char, dot-slash+1);
188 if(name==NULL)
189 goto err1;
191 strncpy(name, slash, dot-slash);
192 name[dot-slash]='\0';
194 if(get_handle(name)){
195 warn_obj(file, TR("The module is already loaded."));
196 goto err2;
199 handle=dlopen(file, RTLD_NOW|RTLD_GLOBAL);
201 if(handle==NULL){
202 warn_obj(file, "%s", dlerror());
203 goto err2;
206 if(get_name(handle))
207 return EXTL_TRYCONFIG_OK;
209 if(!check_has_version(handle, name)){
210 warn_obj(file, TR("Module version information for %s not found. "
211 "Refusing to use."), name);
212 goto err3;
215 if(!check_version(handle, name)){
216 warn_obj(file, TR("Module version mismatch: expected '%s', found '%s'."
217 " Refusing to use."),
218 NOTION_API_VERSION, get_version(handle, name));
219 goto err3;
222 mod=add_module(name, handle);
224 if(mod==NULL)
225 goto err3;
227 if(!call_init(handle, name)){
228 warn_obj(file, TR("Unable to initialise module %s."), name);
229 rb_delete_node(mod);
230 goto err3;
233 return EXTL_TRYCONFIG_OK;
235 err3:
236 dlclose(handle);
237 err2:
238 free(name);
239 err1:
240 return EXTL_TRYCONFIG_LOAD_FAILED;
243 static bool do_load_module(const char *modname)
245 int retval;
246 const char *extension = "so";
248 retval=extl_try_config(modname, NULL, (ExtlTryConfigFn*)try_load,
249 NULL, extension, NULL);
251 if(retval==EXTL_TRYCONFIG_NOTFOUND)
252 warn(TR("Unable to find '%s.%s' on search path."), modname, extension);
254 return (retval==EXTL_TRYCONFIG_OK);
258 /*}}}*/
261 /*{{{ Deinit */
264 static void do_unload_module(Rb_node mod)
266 char *name=(char*)mod->k.key;
267 dlhandle handle=mod->v.val;
269 call_deinit(handle, name);
271 dlclose(handle);
272 free(name);
276 void ioncore_unload_modules()
278 Rb_node mod;
280 rb_traverse(mod, modules){
281 do_unload_module(mod);
286 /*}}}*/
289 #else
292 /*{{{ Static module support */
295 static bool call_init(WStaticModuleInfo *handle)
297 if(handle->init!=NULL)
298 return handle->init();
299 return TRUE;
303 static void call_deinit(WStaticModuleInfo *handle)
305 if(handle->deinit!=NULL)
306 handle->deinit();
310 extern WStaticModuleInfo ioncore_static_modules[];
313 static bool do_load_module(const char *name)
315 WStaticModuleInfo *mod;
317 for(mod=ioncore_static_modules; mod->name!=NULL; mod++){
318 if(strcmp(mod->name, name)==0)
319 break;
322 if(mod->name==NULL){
323 warn_obj(name, TR("Unknown module."));
324 return FALSE;
327 if(mod->loaded)
328 return TRUE;
330 if(!call_init(mod)){
331 warn_obj(name, TR("Unable to initialise module."));
332 return FALSE;
335 mod->loaded=TRUE;
337 return TRUE;
341 void ioncore_unload_modules()
343 WStaticModuleInfo *mod;
345 for(mod=ioncore_static_modules; mod->name!=NULL; mod++){
346 if(mod->loaded){
347 call_deinit(mod);
348 mod->loaded=FALSE;
354 bool ioncore_init_module_support()
356 return TRUE;
360 /*}}}*/
363 #endif
366 /*{{{ Exports */
369 /*EXTL_DOC
370 * Attempt to load a C-side module.
372 EXTL_EXPORT
373 bool ioncore_load_module(const char *modname)
375 if(modname==NULL){
376 warn(TR("No module to load given."));
377 return FALSE;
379 return do_load_module(modname);
383 /*}}}*/