ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/hybrid-ircservices-1/modules.c
Revision: 1209
Committed: Thu Aug 25 19:05:49 2011 UTC (12 years, 7 months ago) by michael
Content type: text/x-csrc
File size: 29711 byte(s)
Log Message:
- run everything thru indent
  "-bli0 -di1 -npcs -nut -cdw -bls -nbbo -bap"

File Contents

# Content
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 */