1 |
/* Module support. |
2 |
* |
3 |
* IRC Services is copyright (c) 1996-2009 Andrew Church. |
4 |
* E-mail: <achurch@achurch.org> |
5 |
* Parts written by Andrew Kempe and others. |
6 |
* This program is free but copyrighted software; see the file GPL.txt for |
7 |
* details. |
8 |
*/ |
9 |
|
10 |
#include "services.h" |
11 |
#include "modules.h" |
12 |
#include "conffile.h" |
13 |
#undef use_module |
14 |
#undef unuse_module |
15 |
|
16 |
#if !STATIC_MODULES |
17 |
# include <dlfcn.h> |
18 |
#endif |
19 |
|
20 |
/*************************************************************************/ |
21 |
|
22 |
/* Internal structure for callbacks. */ |
23 |
typedef struct callbackinfo_ CallbackInfo; |
24 |
struct callbackinfo_ { |
25 |
char *name; |
26 |
int calling; /* used by {call,remove}_callback() for safe callback |
27 |
* removal from inside the callback */ |
28 |
struct { |
29 |
callback_t func; |
30 |
const Module *adder; |
31 |
int pri; |
32 |
} *funcs; |
33 |
int funcs_count; |
34 |
}; |
35 |
|
36 |
/* Structure for module data. */ |
37 |
struct Module_ { |
38 |
Module *next, *prev; |
39 |
char *name; /* Module name (path passed to load_module())*/ |
40 |
ConfigDirective *modconfig; /* `module_config' in this module */ |
41 |
Module ***this_module_pptr; /* `_this_module_ptr' in this module */ |
42 |
const int32 *module_version_ptr; /* `module_version' in this module */ |
43 |
void *dllhandle; /* Handle used by dynamic linker */ |
44 |
CallbackInfo *callbacks; |
45 |
int callbacks_count; |
46 |
const Module **users; /* Array of module's users (use_module()) */ |
47 |
int users_count; |
48 |
}; |
49 |
|
50 |
|
51 |
/* Module data for Services core. */ |
52 |
static Module coremodule = {.name = "core"}; |
53 |
|
54 |
|
55 |
/* Global list of modules. */ |
56 |
static Module *modulelist = &coremodule; |
57 |
|
58 |
|
59 |
/* Callbacks for loading, unloading, and reconfiguring modules. */ |
60 |
static int cb_load_module = -1; |
61 |
static int cb_unload_module = -1; |
62 |
static int cb_reconfigure = -1; |
63 |
|
64 |
|
65 |
/*************************************************************************/ |
66 |
|
67 |
#if STATIC_MODULES |
68 |
|
69 |
/* Structure for a module symbol. */ |
70 |
struct modsym { |
71 |
const char *symname; |
72 |
void *value; |
73 |
}; |
74 |
|
75 |
/* Static module information (from modules/modules.a): */ |
76 |
struct modinfo { |
77 |
const char *modname; |
78 |
struct modsym *modsyms; |
79 |
}; |
80 |
extern struct modinfo modlist[]; |
81 |
|
82 |
#else /* !STATIC_MODULES */ |
83 |
|
84 |
static void *program_handle; /* Handle for the main program */ |
85 |
|
86 |
#endif /* STATIC_MODULES */ |
87 |
|
88 |
/*************************************************************************/ |
89 |
|
90 |
/* Internal routine declarations: */ |
91 |
|
92 |
static Module *internal_load_module(const char *modulename); |
93 |
static int internal_init_module(Module *module); |
94 |
static int internal_unload_module(Module *module, int shutdown); |
95 |
|
96 |
/*************************************************************************/ |
97 |
|
98 |
/* Translate the NULL module to &coremodule, and ensure that any given |
99 |
* module is in fact on the module list, aborting the function otherwise. |
100 |
* Call as: |
101 |
* VALIDATE_MOD(module) |
102 |
* or |
103 |
* VALIDATE_MOD(module, return_value) |
104 |
* where `module' is a variable holding a module handle. |
105 |
*/ |
106 |
|
107 |
#define VALIDATE_MOD(__m,...) do { \ |
108 |
if (!__m) { \ |
109 |
__m = &coremodule; \ |
110 |
} else { \ |
111 |
Module *__tmp; \ |
112 |
LIST_FOREACH (__tmp, modulelist) { \ |
113 |
if (__tmp == __m) \ |
114 |
break; \ |
115 |
} \ |
116 |
if (!__tmp) { \ |
117 |
log("%s(): module %p not on module list", __FUNCTION__, __m); \ |
118 |
return __VA_ARGS__; \ |
119 |
} \ |
120 |
} \ |
121 |
} while (0) |
122 |
|
123 |
/*************************************************************************/ |
124 |
/********************* Initialization and cleanup ************************/ |
125 |
/*************************************************************************/ |
126 |
|
127 |
int modules_init(int ac, char **av) |
128 |
{ |
129 |
#if !STATIC_MODULES |
130 |
program_handle = dlopen(NULL, 0); |
131 |
#endif |
132 |
cb_load_module = register_callback("load module"); |
133 |
cb_unload_module = register_callback("unload module"); |
134 |
cb_reconfigure = register_callback("reconfigure"); |
135 |
if (cb_load_module < 0 || cb_unload_module < 0 || cb_reconfigure < 0) { |
136 |
log("modules_init: register_callback() failed\n"); |
137 |
return 0; |
138 |
} |
139 |
return 1; |
140 |
} |
141 |
|
142 |
/*************************************************************************/ |
143 |
|
144 |
void modules_cleanup(void) |
145 |
{ |
146 |
int i; |
147 |
|
148 |
unload_all_modules(); |
149 |
unregister_callback(cb_reconfigure); |
150 |
unregister_callback(cb_unload_module); |
151 |
unregister_callback(cb_load_module); |
152 |
ARRAY_FOREACH(i, coremodule.callbacks) { |
153 |
if (coremodule.callbacks[i].name) { |
154 |
log("modules: Core forgot to unregister callback `%s'", |
155 |
coremodule.callbacks[i].name); |
156 |
free(coremodule.callbacks[i].name); |
157 |
free(coremodule.callbacks[i].funcs); |
158 |
} |
159 |
} |
160 |
free(coremodule.callbacks); |
161 |
} |
162 |
|
163 |
/*************************************************************************/ |
164 |
|
165 |
void unload_all_modules(void) |
166 |
{ |
167 |
/* Normally it would be sufficient to iterate through the module list, |
168 |
* since new modules are always inserted at the front of the list, but |
169 |
* it is possible for an older module to lock a newer module via the |
170 |
* "load module" callback, resulting in unload failures if that simple |
171 |
* method was used. Instead, we repeatedly search the module list for |
172 |
* the first unloadable module (a module that is not the core module |
173 |
* and is not locked), unload that module, and repeat until no |
174 |
* unloadable modules are left. (In theory, since a module's locks are |
175 |
* forcibly when the module is unloaded, this should only leave the |
176 |
* core module, but we check anyway just to be safe.) */ |
177 |
|
178 |
for (;;) { |
179 |
Module *mod; |
180 |
LIST_FOREACH (mod, modulelist) { |
181 |
if (strcmp(mod->name, "core") != 0) { |
182 |
int i; |
183 |
ARRAY_FOREACH (i, mod->users) { |
184 |
if (mod->users[i] != mod) |
185 |
break; |
186 |
} |
187 |
if (i >= mod->users_count) { |
188 |
/* This module has no users (except possibly itself), |
189 |
* so unload it */ |
190 |
break; |
191 |
} |
192 |
} |
193 |
} |
194 |
if (!mod) |
195 |
break; |
196 |
if (!internal_unload_module(mod, 1)) { |
197 |
log("modules: Failed to unload `%s' on exit", mod->name); |
198 |
/* Unlink it anyway, but don't free the structure to avoid |
199 |
* segfaults in the module. This is an impossible case |
200 |
* anyway, so we don't worry about the leak. */ |
201 |
LIST_REMOVE(mod, modulelist); |
202 |
} |
203 |
} |
204 |
|
205 |
if (!modulelist) { |
206 |
log("modules: BUG: core module got removed from module list during" |
207 |
" shutdown!"); |
208 |
} else if (modulelist != &coremodule || modulelist->next != NULL) { |
209 |
Module *mod; |
210 |
log("modules: BUG: failed to unload some modules during shutdown" |
211 |
" (circular lock?)"); |
212 |
LIST_FOREACH (mod, modulelist) { |
213 |
if (strcmp(mod->name, "core") != 0) { |
214 |
log("modules: -- module %s not unloaded", mod->name); |
215 |
} |
216 |
} |
217 |
} |
218 |
} |
219 |
|
220 |
/*************************************************************************/ |
221 |
/*********************** Low-level module routines ***********************/ |
222 |
/*************************************************************************/ |
223 |
|
224 |
/* These low-level routines take care of all changes in processing with |
225 |
* regard to dynamic vs. static modules and different platforms. */ |
226 |
|
227 |
/* Common variables: */ |
228 |
|
229 |
#if STATIC_MODULES |
230 |
static const char *dl_last_error; |
231 |
#endif |
232 |
|
233 |
/*************************************************************************/ |
234 |
|
235 |
/* Low-level routine to open a module and return a handle. */ |
236 |
|
237 |
static void *my_dlopen(const char *name) |
238 |
{ |
239 |
#if !STATIC_MODULES |
240 |
|
241 |
char pathname[PATH_MAX+1]; |
242 |
snprintf(pathname, sizeof(pathname), "%s/modules/%s.so", |
243 |
services_dir, name); |
244 |
return dlopen(pathname, RTLD_NOW | RTLD_GLOBAL); |
245 |
|
246 |
#else /* STATIC_MODULES */ |
247 |
|
248 |
int i; |
249 |
|
250 |
for (i = 0; modlist[i].modname; i++) { |
251 |
if (strcmp(modlist[i].modname, name) == 0) |
252 |
break; |
253 |
} |
254 |
if (!modlist[i].modname) { |
255 |
dl_last_error = "Module not found"; |
256 |
return NULL; |
257 |
} |
258 |
return &modlist[i]; |
259 |
|
260 |
#endif /* STATIC_MODULES */ |
261 |
} /* my_dlopen() */ |
262 |
|
263 |
/*************************************************************************/ |
264 |
|
265 |
/* Low-level routine to close a module. */ |
266 |
|
267 |
static void my_dlclose(void *handle) |
268 |
{ |
269 |
#if !STATIC_MODULES |
270 |
|
271 |
dlclose(handle); |
272 |
|
273 |
#else /* STATIC_MODULES */ |
274 |
|
275 |
/* nothing */ |
276 |
|
277 |
#endif /* STATIC_MODULES */ |
278 |
} /* my_dlclose() */ |
279 |
|
280 |
/*************************************************************************/ |
281 |
|
282 |
/* Low-level routine to retrieve a symbol from a module given its handle. */ |
283 |
|
284 |
static void *my_dlsym(void *handle, const char *symname) |
285 |
{ |
286 |
#if !STATIC_MODULES |
287 |
|
288 |
# if SYMS_NEED_UNDERSCORES |
289 |
char buf[256]; |
290 |
if (strlen(symname) > sizeof(buf)-2) { /* too long for buffer */ |
291 |
log("modules: symbol name too long in my_dlsym(): %s", symname); |
292 |
return NULL; |
293 |
} |
294 |
snprintf(buf, sizeof(buf), "_%s", symname); |
295 |
symname = buf; |
296 |
# endif |
297 |
if (handle) { |
298 |
return dlsym(handle, symname); |
299 |
} else { |
300 |
Module *mod; |
301 |
void *ptr; |
302 |
LIST_FOREACH (mod, modulelist) { |
303 |
ptr = dlsym(mod->dllhandle?mod->dllhandle:program_handle, symname); |
304 |
if (ptr) |
305 |
return ptr; |
306 |
} |
307 |
return NULL; |
308 |
} |
309 |
|
310 |
#else /* STATIC_MODULES */ |
311 |
|
312 |
int i; |
313 |
|
314 |
if (handle) { |
315 |
struct modsym *syms = ((struct modinfo *)handle)->modsyms; |
316 |
for (i = 0; syms[i].symname; i++) { |
317 |
if (strcmp(syms[i].symname, symname) == 0) |
318 |
break; |
319 |
} |
320 |
if (!syms[i].symname) { |
321 |
dl_last_error = "Symbol not found"; |
322 |
return NULL; |
323 |
} |
324 |
return syms[i].value; |
325 |
} else { |
326 |
const char *save_dl_last_error = dl_last_error; |
327 |
for (i = 0; modlist[i].modname; i++) { |
328 |
void *value = my_dlsym(&modlist[i], symname); |
329 |
if (value) { |
330 |
dl_last_error = save_dl_last_error; |
331 |
return value; |
332 |
} |
333 |
} |
334 |
dl_last_error = "Symbol not found"; |
335 |
return NULL; |
336 |
} |
337 |
|
338 |
#endif /* STATIC_MODULES */ |
339 |
} /* my_dlsym() */ |
340 |
|
341 |
/*************************************************************************/ |
342 |
|
343 |
/* Low-level routine to return the error message (if any) from the previous |
344 |
* call. */ |
345 |
|
346 |
static const char *my_dlerror(void) |
347 |
{ |
348 |
#if !STATIC_MODULES |
349 |
|
350 |
return dlerror(); |
351 |
|
352 |
#else /* STATIC_MODULES */ |
353 |
|
354 |
const char *str = dl_last_error; |
355 |
dl_last_error = NULL; |
356 |
return str; |
357 |
|
358 |
#endif /* STATIC_MODULES */ |
359 |
} /* my_dlerror() */ |
360 |
|
361 |
/*************************************************************************/ |
362 |
/************************ Module-level functions *************************/ |
363 |
/*************************************************************************/ |
364 |
|
365 |
/* Load a new module and return the Module pointer, or NULL on error. |
366 |
* (External interface to the above functions.) |
367 |
*/ |
368 |
|
369 |
Module *load_module(const char *modulename) |
370 |
{ |
371 |
Module *module; |
372 |
|
373 |
|
374 |
if (!modulename) { |
375 |
log("load_module(): modulename is NULL!"); |
376 |
return NULL; |
377 |
} |
378 |
|
379 |
log_debug(1, "Loading module `%s'", modulename); |
380 |
|
381 |
module = internal_load_module(modulename); |
382 |
if (!module) |
383 |
return NULL; |
384 |
LIST_INSERT(module, modulelist); |
385 |
|
386 |
if (!configure(module->name, module->modconfig, |
387 |
CONFIGURE_READ | CONFIGURE_SET)) { |
388 |
log("modules: configure() failed for %s", modulename); |
389 |
goto fail; |
390 |
} |
391 |
|
392 |
if (!internal_init_module(module)) { |
393 |
log("modules: init_module() failed for %s", modulename); |
394 |
deconfigure(module->modconfig); |
395 |
goto fail; |
396 |
} |
397 |
|
398 |
log_debug(1, "Successfully loaded module `%s'", modulename); |
399 |
call_callback_2(cb_load_module, module, module->name); |
400 |
|
401 |
/* This is a REALLY STUPID HACK to ensure protocol modules are loaded |
402 |
* first--but hey, it works! */ |
403 |
if (protocol_features & PF_UNSET) { |
404 |
log("FATAL: load_module(): A protocol module must be loaded first!"); |
405 |
unload_module(module); |
406 |
return NULL; |
407 |
} |
408 |
|
409 |
return module; |
410 |
|
411 |
fail: |
412 |
free(module->name); |
413 |
my_dlclose(module->dllhandle); |
414 |
LIST_REMOVE(module, modulelist); |
415 |
free(module); |
416 |
return NULL; |
417 |
} |
418 |
|
419 |
/************************************/ |
420 |
|
421 |
/* Internal routine to load a module. Returns the module pointer or NULL |
422 |
* on error. |
423 |
*/ |
424 |
|
425 |
static Module *internal_load_module(const char *modulename) |
426 |
{ |
427 |
void *handle; |
428 |
Module *module, *mptr; |
429 |
int32 *verptr; |
430 |
Module ***thisptr; |
431 |
ConfigDirective *confptr; |
432 |
|
433 |
if (strstr(modulename, "../")) { |
434 |
log("modules: Attempt to load bad module name: %s", modulename); |
435 |
goto err_return; |
436 |
} |
437 |
LIST_SEARCH(modulelist, name, modulename, strcmp, mptr); |
438 |
if (mptr) { |
439 |
log("modules: Attempt to load module `%s' twice", modulename); |
440 |
goto err_return; |
441 |
} |
442 |
|
443 |
handle = my_dlopen(modulename); |
444 |
if (!handle) { |
445 |
const char *error = my_dlerror(); |
446 |
if (!error) |
447 |
error = "Unknown error"; |
448 |
log("modules: Unable to load module `%s': %s", modulename, error); |
449 |
goto err_return; |
450 |
} |
451 |
|
452 |
module = scalloc(sizeof(*module), 1); |
453 |
module->dllhandle = handle; |
454 |
module->name = sstrdup(modulename); |
455 |
|
456 |
thisptr = NULL; |
457 |
if (check_module_symbol(module, "_this_module_ptr", (void **)&thisptr, |
458 |
NULL)){ |
459 |
#if !defined(STATIC_MODULES) |
460 |
/* When using dynamic linking, the above may return the first |
461 |
* instance of `_this_module_ptr' found in _any_ module (though |
462 |
* giving priority to the given module), so we need to check if |
463 |
* we've seen this address before. With static linking, the result |
464 |
* will be NULL if the symbol does not exist in this specific |
465 |
* module, so the extra check is unnecessary. */ |
466 |
LIST_SEARCH_SCALAR(modulelist, this_module_pptr, thisptr, mptr); |
467 |
if (mptr) |
468 |
thisptr = NULL; |
469 |
#endif |
470 |
} |
471 |
if (!thisptr) { |
472 |
log("modules: Unable to load module `%s': No `_this_module_ptr' symbol" |
473 |
" found", modulename); |
474 |
goto err_freemod; |
475 |
} |
476 |
module->this_module_pptr = thisptr; |
477 |
**thisptr = module; |
478 |
|
479 |
verptr = NULL; /* as above */ |
480 |
if (check_module_symbol(module, "module_version", (void **)&verptr, NULL)){ |
481 |
#if !defined(STATIC_MODULES) |
482 |
LIST_SEARCH_SCALAR(modulelist, module_version_ptr, verptr, mptr); |
483 |
if (mptr) |
484 |
verptr = NULL; |
485 |
#endif |
486 |
} |
487 |
if (!verptr) { |
488 |
log("modules: Unable to load module `%s': No `module_version'" |
489 |
" symbol found", modulename); |
490 |
goto err_freemod; |
491 |
} else if (*verptr != MODULE_VERSION_CODE) { |
492 |
log("modules: Unable to load module `%s': Version mismatch" |
493 |
" (module version = %08X, core version = %08X)", |
494 |
modulename, *verptr, MODULE_VERSION_CODE); |
495 |
goto err_freemod; |
496 |
} |
497 |
module->module_version_ptr = verptr; |
498 |
|
499 |
confptr = NULL; /* as above */ |
500 |
if (check_module_symbol(module, "module_config", (void **)&confptr, NULL)){ |
501 |
#if !defined(STATIC_MODULES) |
502 |
LIST_SEARCH_SCALAR(modulelist, modconfig, confptr, mptr); |
503 |
if (mptr) |
504 |
confptr = NULL; |
505 |
#endif |
506 |
} |
507 |
module->modconfig = confptr; |
508 |
|
509 |
return module; |
510 |
|
511 |
err_freemod: |
512 |
free(module->name); |
513 |
free(module); |
514 |
my_dlclose(handle); |
515 |
err_return: |
516 |
return NULL; |
517 |
} |
518 |
|
519 |
/************************************/ |
520 |
|
521 |
/* Initialize a module. Return the module's init_module() return value, or |
522 |
* 1 if the module does not have an init_module() function. |
523 |
*/ |
524 |
|
525 |
static int internal_init_module(Module *module) |
526 |
{ |
527 |
int (*initfunc)(void); |
528 |
|
529 |
initfunc = get_module_symbol(module, "init_module"); |
530 |
if (initfunc) |
531 |
return initfunc(); |
532 |
else |
533 |
return 1; |
534 |
} |
535 |
|
536 |
/*************************************************************************/ |
537 |
|
538 |
/* Remove a module from memory. Return nonzero on success, zero on |
539 |
* failure. |
540 |
*/ |
541 |
|
542 |
int unload_module(Module *module) |
543 |
{ |
544 |
return internal_unload_module(module, 0); |
545 |
} |
546 |
|
547 |
/************************************/ |
548 |
|
549 |
/* Internal implementation of unload_module(), taking an additional |
550 |
* parameter indicating whether the unload is due to Services shutting down |
551 |
* or not. |
552 |
*/ |
553 |
|
554 |
static int internal_unload_module(Module *module, int shutdown) |
555 |
{ |
556 |
int (*exit_module)(int shutdown); |
557 |
Module *tmp; |
558 |
int i; |
559 |
|
560 |
|
561 |
if (!module) { |
562 |
log("unload_module(): module is NULL!"); |
563 |
return 0; |
564 |
} |
565 |
|
566 |
if (module->users_count > 0) { |
567 |
log("modules: Attempt to unload in-use module `%s' (in use by %s%s)", |
568 |
module->name, module->users[0]->name, |
569 |
module->users_count>1 ? " and others" : ""); |
570 |
return 0; |
571 |
} |
572 |
|
573 |
log_debug(1, "Unloading module `%s'", module->name); |
574 |
|
575 |
/* Call the module's exit routine */ |
576 |
exit_module = get_module_symbol(module, "exit_module"); |
577 |
if (exit_module && !(*exit_module)(shutdown)) { |
578 |
if (shutdown) { |
579 |
log("modules: exit_module() for module `%s' returned zero on" |
580 |
" shutdown, unloading module anyway", module->name); |
581 |
} else { |
582 |
return 0; |
583 |
} |
584 |
} |
585 |
|
586 |
/* Remove the module from the global module list, ensuring that no |
587 |
* new callbacks or the like can be added while we unload it */ |
588 |
LIST_REMOVE(module, modulelist); |
589 |
|
590 |
/* Ensure that callbacks and use_module() calls are properly undone */ |
591 |
LIST_FOREACH (tmp, modulelist) { |
592 |
ARRAY_FOREACH (i, tmp->users) { |
593 |
if (tmp->users[i] == module) { |
594 |
log("modules: Module `%s' forgot to unuse_module() for" |
595 |
" module `%s'", module->name, tmp->name); |
596 |
ARRAY_REMOVE(tmp->users, i); |
597 |
i--; |
598 |
} |
599 |
} |
600 |
ARRAY_FOREACH (i, tmp->callbacks) { |
601 |
int j; |
602 |
/* Don't warn for callbacks that couldn't have been removed |
603 |
* because the callback was in use (e.g. for shutting down |
604 |
* after a crash) */ |
605 |
if (tmp->callbacks[i].calling) |
606 |
continue; |
607 |
ARRAY_FOREACH (j, tmp->callbacks[i].funcs) { |
608 |
if (tmp->callbacks[i].funcs[j].adder == module) { |
609 |
log("modules: Module `%s' forgot to remove callback" |
610 |
" `%s' from module `%s'", module->name, |
611 |
tmp->callbacks[i].name, tmp->name); |
612 |
ARRAY_REMOVE(tmp->callbacks[i].funcs, j); |
613 |
j--; |
614 |
} |
615 |
} |
616 |
} |
617 |
} |
618 |
ARRAY_FOREACH (i, module->callbacks) { |
619 |
if (module->callbacks[i].name) { |
620 |
log("modules: Module `%s' forgot to unregister callback `%s'", |
621 |
module->name, module->callbacks[i].name); |
622 |
free(module->callbacks[i].name); |
623 |
free(module->callbacks[i].funcs); |
624 |
} |
625 |
} |
626 |
free(module->callbacks); |
627 |
|
628 |
/* Clean up and free the module data */ |
629 |
call_callback_1(cb_unload_module, module); |
630 |
deconfigure(module->modconfig); |
631 |
free(module->name); |
632 |
my_dlclose(module->dllhandle); |
633 |
free(module); |
634 |
|
635 |
return 1; |
636 |
} |
637 |
|
638 |
/*************************************************************************/ |
639 |
|
640 |
/* Return the Module pointer for the named module, or NULL if no such |
641 |
* module exists. |
642 |
*/ |
643 |
|
644 |
Module *find_module(const char *modulename) |
645 |
{ |
646 |
Module *result; |
647 |
|
648 |
if (!modulename) { |
649 |
log("find_module(): modulename is NULL!"); |
650 |
return NULL; |
651 |
} |
652 |
LIST_SEARCH(modulelist, name, modulename, strcmp, result); |
653 |
return result; |
654 |
} |
655 |
|
656 |
/*************************************************************************/ |
657 |
|
658 |
/* Increment the use count for the given module. A module cannot be |
659 |
* unloaded while its use count is nonzero. |
660 |
*/ |
661 |
|
662 |
static int use_module_loopcheck(const Module *module, const Module *check) |
663 |
{ |
664 |
/* Return whether `module' is used by `check' (self-references are |
665 |
* ignored). */ |
666 |
|
667 |
int i; |
668 |
|
669 |
ARRAY_FOREACH (i, module->users) { |
670 |
if (module->users[i] != module) { |
671 |
if (module->users[i] == check |
672 |
|| use_module_loopcheck(module->users[i], check)) |
673 |
return 1; |
674 |
} |
675 |
} |
676 |
return 0; |
677 |
} |
678 |
|
679 |
void _use_module(Module *module, const Module *caller) |
680 |
{ |
681 |
VALIDATE_MOD(module); |
682 |
VALIDATE_MOD(caller); |
683 |
if (module == caller) { |
684 |
log("modules: BUG: Module `%s' called use_module() for itself!", |
685 |
module->name); |
686 |
return; |
687 |
} |
688 |
if (use_module_loopcheck(caller, module)) { |
689 |
log("modules: BUG: use_module loop detected (called by `%s' for `%s')", |
690 |
caller->name, module->name); |
691 |
return; |
692 |
} |
693 |
ARRAY_EXTEND(module->users); |
694 |
module->users[module->users_count-1] = caller; |
695 |
} |
696 |
|
697 |
/*************************************************************************/ |
698 |
|
699 |
/* Decrement the use count for the given module. `module' may be NULL, in |
700 |
* which case this routine does nothing. |
701 |
*/ |
702 |
|
703 |
void _unuse_module(Module *module, const Module *caller) |
704 |
{ |
705 |
int i; |
706 |
|
707 |
if (!module) |
708 |
return; |
709 |
VALIDATE_MOD(module); |
710 |
VALIDATE_MOD(caller); |
711 |
if (module == caller) { |
712 |
log("modules: BUG: Module `%s' called unuse_module() for itself!", |
713 |
module->name); |
714 |
return; |
715 |
} |
716 |
if (module->users_count == 0) { |
717 |
log("modules: BUG: trying to unuse module `%s' with use count 0" |
718 |
" from module `%s'", module->name, caller->name); |
719 |
return; |
720 |
} |
721 |
ARRAY_SEARCH_PLAIN_SCALAR(module->users, caller, i); |
722 |
if (i >= module->users_count) { |
723 |
log("modules: BUG: trying to unuse module `%s' from module `%s' but" |
724 |
" caller not found in user list!", module->name, caller->name); |
725 |
return; |
726 |
} |
727 |
ARRAY_REMOVE(module->users, i); |
728 |
} |
729 |
|
730 |
/*************************************************************************/ |
731 |
|
732 |
/* Reconfigure all modules. The "reconfigure" callback is called with an |
733 |
* `int' parameter of 0 before reconfiguration and 1 after. Returns 1 on |
734 |
* success, 0 on failure (on failure, all modules' configuration data will |
735 |
* be left alone). |
736 |
*/ |
737 |
|
738 |
int reconfigure_modules(void) |
739 |
{ |
740 |
Module *mod; |
741 |
|
742 |
call_callback_1(cb_reconfigure, 0); |
743 |
LIST_FOREACH (mod, modulelist) { |
744 |
if (!configure(mod->name, mod->modconfig, CONFIGURE_READ)) |
745 |
return 0; |
746 |
} |
747 |
LIST_FOREACH (mod, modulelist) |
748 |
configure(mod->name, mod->modconfig, CONFIGURE_SET); |
749 |
call_callback_1(cb_reconfigure, 1); |
750 |
return 1; |
751 |
} |
752 |
|
753 |
/*************************************************************************/ |
754 |
/****************** Module symbol/information retrieval ******************/ |
755 |
/*************************************************************************/ |
756 |
|
757 |
/* Retrieve the value of the named symbol in the given module. Return NULL |
758 |
* if no such symbol exists. Note that this function should not be used |
759 |
* for symbols whose value might be NULL, because there is no way to |
760 |
* distinguish a symbol value of NULL from an error return. For such |
761 |
* symbols, or for cases where a symbol might legitimately not exist and |
762 |
* no error should be printed for nonexistence, use check_module_symbol(). |
763 |
*/ |
764 |
|
765 |
void *_get_module_symbol(Module *module, const char *symname, |
766 |
const Module *caller) |
767 |
{ |
768 |
void *value; |
769 |
|
770 |
if (!check_module_symbol(module, symname, &value, NULL)) { |
771 |
if (module) { |
772 |
log("%s: Unable to resolve symbol `%s' in module `%s'", |
773 |
get_module_name(caller), symname, get_module_name(module)); |
774 |
} else { |
775 |
log("%s: Unable to resolve symbol `%s'", |
776 |
get_module_name(caller), symname); |
777 |
} |
778 |
return NULL; |
779 |
} |
780 |
return value; |
781 |
} |
782 |
|
783 |
/*************************************************************************/ |
784 |
|
785 |
/* Check whether the given symbol exists in the given module; return 1 if |
786 |
* so, 0 otherwise. If `resultptr' is non-NULL and the symbol exists, the |
787 |
* value is stored in the variable it points to. If `errorptr' is non-NULL |
788 |
* and the symbol does not exist, a human-readable error message is stored |
789 |
* in the variable it points to. |
790 |
*/ |
791 |
|
792 |
int check_module_symbol(Module *module, const char *symname, |
793 |
void **resultptr, const char **errorptr) |
794 |
{ |
795 |
void *value; |
796 |
const char *error; |
797 |
|
798 |
(void) my_dlerror(); /* clear any previous error */ |
799 |
value = my_dlsym(module ? module->dllhandle : NULL, symname); |
800 |
error = my_dlerror(); |
801 |
if (error) { |
802 |
if (errorptr) |
803 |
*errorptr = error; |
804 |
return 0; |
805 |
} else { |
806 |
if (resultptr) |
807 |
*resultptr = value; |
808 |
return 1; |
809 |
} |
810 |
} |
811 |
|
812 |
/*************************************************************************/ |
813 |
|
814 |
/* Retrieve the name of the given module. If NULL is given, returns the |
815 |
* string "core". |
816 |
*/ |
817 |
|
818 |
const char *get_module_name(const Module *module) |
819 |
{ |
820 |
return module ? module->name : "core"; |
821 |
} |
822 |
|
823 |
/*************************************************************************/ |
824 |
/********************** Callback-related functions ***********************/ |
825 |
/*************************************************************************/ |
826 |
|
827 |
/* Local function to look up a callback for a module. Returns NULL if not |
828 |
* found. |
829 |
*/ |
830 |
|
831 |
static CallbackInfo *find_callback(Module *module, const char *name) |
832 |
{ |
833 |
int i; |
834 |
|
835 |
ARRAY_FOREACH (i, module->callbacks) { |
836 |
if (module->callbacks[i].name |
837 |
&& strcmp(module->callbacks[i].name,name) == 0) |
838 |
break; |
839 |
} |
840 |
if (i == module->callbacks_count) |
841 |
return NULL; |
842 |
return &module->callbacks[i]; |
843 |
} |
844 |
|
845 |
/*************************************************************************/ |
846 |
|
847 |
/* Register a new callback. "module" is the calling module's own Module |
848 |
* pointer, or NULL for core Services callbacks (this is set by the |
849 |
* register_callback() macro). Return the callback identifier (a |
850 |
* nonnegative integer) or -1 on error. |
851 |
*/ |
852 |
|
853 |
int _register_callback(Module *module, const char *name) |
854 |
{ |
855 |
int i; |
856 |
|
857 |
log_debug(2, "register_callback(%s, \"%s\")", |
858 |
module ? module->name : "core", name); |
859 |
VALIDATE_MOD(module, -1); |
860 |
if (find_callback(module, name)) { |
861 |
log("BUG: register_callback(%s,\"%s\"): callback already registered", |
862 |
module ? module->name : "core", name); |
863 |
return -1; |
864 |
} |
865 |
i = module->callbacks_count; |
866 |
ARRAY_EXTEND(module->callbacks); |
867 |
module->callbacks[i].name = sstrdup(name); |
868 |
module->callbacks[i].calling = 0; |
869 |
module->callbacks[i].funcs_count = 0; |
870 |
module->callbacks[i].funcs = NULL; |
871 |
return i; |
872 |
} |
873 |
|
874 |
/*************************************************************************/ |
875 |
|
876 |
/* Call all functions hooked into a callback. Return 1 if a callback |
877 |
* returned nonzero, 0 if all callbacks returned zero, or -1 on error. |
878 |
*/ |
879 |
|
880 |
int _call_callback_5(Module *module, int id, void *arg1, void *arg2, |
881 |
void *arg3, void *arg4, void *arg5) |
882 |
{ |
883 |
CallbackInfo *cl; |
884 |
int res = 0; |
885 |
int i; |
886 |
|
887 |
VALIDATE_MOD(module, -1); |
888 |
if (id < 0 || id >= module->callbacks_count) |
889 |
return -1; |
890 |
cl = &module->callbacks[id]; |
891 |
cl->calling = 1; |
892 |
ARRAY_FOREACH (i, cl->funcs) { |
893 |
res = cl->funcs[i].func(arg1, arg2, arg3, arg4, arg5); |
894 |
if (res != 0) |
895 |
break; |
896 |
} |
897 |
if (cl->calling == 2) { /* flag indicating some callbacks were removed */ |
898 |
ARRAY_FOREACH (i, cl->funcs) { |
899 |
if (!cl->funcs[i].func) { |
900 |
ARRAY_REMOVE(cl->funcs, i); |
901 |
i--; |
902 |
} |
903 |
} |
904 |
} |
905 |
cl->calling = 0; |
906 |
return res; |
907 |
} |
908 |
|
909 |
/*************************************************************************/ |
910 |
|
911 |
/* Delete a callback. */ |
912 |
|
913 |
int _unregister_callback(Module *module, int id) |
914 |
{ |
915 |
CallbackInfo *cl; |
916 |
|
917 |
VALIDATE_MOD(module, 0); |
918 |
log_debug(2, "unregister_callback(%s, %d)", module->name, id); |
919 |
if (id < 0 || id >= module->callbacks_count) { |
920 |
log("unregister_callback(): BUG: invalid callback ID %d for module" |
921 |
" `%s'", id, module->name); |
922 |
return 0; |
923 |
} |
924 |
cl = &module->callbacks[id]; |
925 |
if (!cl->name) { |
926 |
log("unregister_callback(): BUG: callback ID %d for module `%s'" |
927 |
" is unused (double unregister?)", id, module->name); |
928 |
return 0; |
929 |
} |
930 |
free(cl->funcs); |
931 |
free(cl->name); |
932 |
cl->funcs = NULL; |
933 |
cl->name = NULL; |
934 |
return 1; |
935 |
} |
936 |
|
937 |
/*************************************************************************/ |
938 |
|
939 |
/* Add (hook) a function into to a callback with the given priority (higher |
940 |
* priority value = called sooner). Callbacks with the same priority are |
941 |
* called in the order they were added. |
942 |
*/ |
943 |
|
944 |
int _add_callback_pri(Module *module, const char *name, callback_t callback, |
945 |
int priority, const Module *caller) |
946 |
{ |
947 |
CallbackInfo *cl; |
948 |
int n; |
949 |
|
950 |
log_debug(2, "add_callback_pri(%s, \"%s\", %p, %d)", |
951 |
module ? module->name : "core", name ? name : "(null)", |
952 |
callback, priority); |
953 |
VALIDATE_MOD(module, 0); |
954 |
VALIDATE_MOD(caller, 0); |
955 |
cl = find_callback(module, name); |
956 |
if (!cl) { |
957 |
log_debug(2, "-- callback not found"); |
958 |
return 0; |
959 |
} |
960 |
if (priority < CBPRI_MIN || priority > CBPRI_MAX) { |
961 |
log("add_callback_pri(): priority (%d) out of range for callback" |
962 |
" `%s' in module `%s'", |
963 |
priority, name, module ? module->name : "core"); |
964 |
return 0; |
965 |
} |
966 |
ARRAY_FOREACH (n, cl->funcs) { |
967 |
if (cl->funcs[n].pri < priority) |
968 |
break; |
969 |
} |
970 |
ARRAY_INSERT(cl->funcs, n); |
971 |
cl->funcs[n].func = callback; |
972 |
cl->funcs[n].adder = caller; |
973 |
cl->funcs[n].pri = priority; |
974 |
return 1; |
975 |
} |
976 |
|
977 |
/*************************************************************************/ |
978 |
|
979 |
/* Remove (unhook) a function from a callback. */ |
980 |
int _remove_callback(Module *module, const char *name, callback_t callback, |
981 |
const Module *caller) |
982 |
{ |
983 |
CallbackInfo *cl; |
984 |
int index; |
985 |
|
986 |
log_debug(2, "remove_callback(%s, \"%s\", %p)", |
987 |
module ? module->name : "core", name, callback); |
988 |
VALIDATE_MOD(module, 0); |
989 |
VALIDATE_MOD(caller, 0); |
990 |
cl = find_callback(module, name); |
991 |
if (!cl) |
992 |
return 0; |
993 |
ARRAY_SEARCH_SCALAR(cl->funcs, func, callback, index); |
994 |
if (index == cl->funcs_count) |
995 |
return 0; |
996 |
if (cl->funcs[index].adder != caller) { |
997 |
log("remove_callback(%s, \"%s\"): BUG: caller `%s' tried to remove" |
998 |
" callback %p added by different module `%s'!", |
999 |
get_module_name(module), name, get_module_name(caller), callback, |
1000 |
get_module_name(cl->funcs[index].adder)); |
1001 |
return 0; |
1002 |
} |
1003 |
if (cl->calling) { |
1004 |
cl->funcs[index].func = NULL; |
1005 |
cl->calling = 2; /* flag to call_callback() indicating CB removed */ |
1006 |
} else { |
1007 |
ARRAY_REMOVE(cl->funcs, index); |
1008 |
} |
1009 |
return 1; |
1010 |
} |
1011 |
|
1012 |
/*************************************************************************/ |
1013 |
|
1014 |
/* |
1015 |
* Local variables: |
1016 |
* c-file-style: "stroustrup" |
1017 |
* c-file-offsets: ((case-label . *) (statement-case-intro . *)) |
1018 |
* indent-tabs-mode: nil |
1019 |
* End: |
1020 |
* |
1021 |
* vim: expandtab shiftwidth=4: |
1022 |
*/ |