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