terom@54: #ifndef MODULE_H terom@54: #define MODULE_H terom@54: terom@54: /** terom@54: * @file terom@54: * terom@55: * Dynamically loadable modules for use with nexus. terom@55: * terom@55: * The modules are loaded using dlopen(), and hence should be standard dynamic libraries. Module initialization happens terom@55: * using a module_init_func_t named "_init", which should return some kind of context pointer, which can later be terom@55: * used to perform other operations on the module. terom@54: */ terom@54: #include "nexus.h" terom@54: #include "error.h" terom@54: terom@54: #include terom@54: terom@54: /** terom@54: * Information required to load/identify a module. terom@54: */ terom@54: struct module_info { terom@54: /** Human-readable name */ terom@54: const char *name; terom@54: terom@54: /** Filesystem path to the .so */ terom@54: const char *path; terom@54: }; terom@54: terom@54: /** terom@57: * A module's behaviour is defined as a set of function pointers, which is dynamically resolved from the module DSO, terom@57: * using the _funcs symbol. terom@57: */ terom@57: struct module_funcs { terom@57: /** terom@57: * Initialize the module, returning an opaque context pointer that is stored in the module state, and supplied for terom@57: * subsequent calls. The supplied nexus arg can be used to access the global state. terom@57: * terom@57: * @param nexus a pointer to the nexus struct containing the global state terom@57: * @param ctx_ptr the context pointer should be returned via this terom@57: * @param err returned error info terom@57: */ terom@57: err_t (*init) (struct nexus *nexus, void **ctx_ptr, struct error_info *err); terom@57: terom@57: /** terom@57: * Set a configuration option with the given name and value, given by the user. Configuration settings are not terom@57: * regarded as unique-per-name, but rather, several invocations of 'conf' with the same name and different values terom@57: * could set up a multitude of things. terom@57: * terom@66: * The given value is either a NUL-terminated string value (which may be mutated, using e.g. strsep), or NULL if terom@66: * no value was given (flag option). terom@57: * terom@57: * @param ctx the module's context pointer as returned by init terom@57: * @param name the name of the configuration setting terom@57: * @param value the value of the configuration setting terom@57: * @param err returned error info terom@57: */ terom@57: err_t (*conf) (void *ctx, const char *name, char *value, struct error_info *err); terom@70: terom@70: /** terom@70: * Unload the module, removing all handlers/callbacks added to the nexus' irc_client. This does not have to act terom@70: * immediately, and will still have access to all irc_* resources it had earlier, and may perform I/O to unload terom@70: * cleanly. But the unloading should complete reasonably quickly, after which all event handlers added by the terom@70: * module to the nexus' ev_base should have been removed, and resources released. terom@70: * terom@70: * @param ctx the module's context pointer as returned by init terom@70: */ terom@70: err_t (*unload) (void *ctx); terom@57: }; terom@57: terom@57: /** terom@54: * A loaded module. terom@54: */ terom@54: struct module { terom@54: /** The identifying info for the module */ terom@54: struct module_info info; terom@54: terom@55: /** The dlopen handle */ terom@55: void *handle; terom@55: terom@57: /** The resolved function table */ terom@57: struct module_funcs *funcs; terom@57: terom@54: /** The module context object */ terom@54: void *ctx; terom@54: terom@70: /** Reference back to modules struct used to load this module */ terom@70: struct modules *modules; terom@70: terom@54: /** Our entry in the list of modules */ terom@55: TAILQ_ENTRY(module) modules_list; terom@54: }; terom@54: terom@54: /** terom@54: * A set of loaded modules, and functionality to load more terom@54: */ terom@54: struct modules { terom@54: /** The nexus in use */ terom@54: struct nexus *nexus; terom@54: terom@54: /** List of loaded modules */ terom@55: TAILQ_HEAD(module_ctx_modules, module) list; terom@55: }; terom@55: terom@55: /** terom@55: * Possible error codes terom@55: */ terom@55: enum module_error_code { terom@55: _ERR_MODULE_BEGIN = _ERR_MODULE, terom@55: terom@55: ERR_MODULE_OPEN, ///< dlopen() failed terom@55: ERR_MODULE_NAME, ///< invalid module_info.name terom@55: ERR_MODULE_SYM, ///< invalid symbol terom@55: ERR_MODULE_INIT_FUNC, ///< invalid module_init_func_t terom@56: ERR_MODULE_CONF, ///< value error in configuration data terom@54: }; terom@54: terom@55: terom@55: /** terom@55: * Maximum length of a module name terom@55: */ terom@55: #define MODULE_NAME_MAX 24 terom@55: terom@55: /** terom@55: * Maximum length of module symbol suffix terom@55: */ terom@55: #define MODULE_SUFFIX_MAX 16 terom@55: terom@55: /** terom@55: * Maximum length of symbol name name, including terminating NUL terom@55: */ terom@55: #define MODULE_SYMBOL_MAX (MODULE_NAME_MAX + 1 + MODULE_SUFFIX_MAX + 1) terom@54: terom@54: /** terom@54: * Create a new modules state terom@54: */ terom@54: err_t modules_create (struct modules **modules_ptr, struct nexus *nexus); terom@54: terom@54: /** terom@55: * Load a new module terom@65: * terom@65: * @param modules the module-loading context terom@65: * @param module_ptr return the new module via this, if not NULL terom@65: * @param info the info required to identify and load the module terom@65: * @param err return error info terom@54: */ terom@56: err_t module_load (struct modules *modules, struct module **module_ptr, const struct module_info *info, struct error_info *err); terom@55: terom@55: /** terom@66: * Lookup a module by name terom@66: * terom@66: * @param modules the modules state terom@66: * @param name the module name to get terom@66: * @return the module struct, or NULL if not found terom@66: */ terom@66: struct module* module_get (struct modules *modules, const char *name); terom@66: terom@66: /** terom@55: * Set a module configuration option terom@55: */ terom@56: err_t module_conf (struct module *module, const char *name, char *value, struct error_info *err); terom@54: terom@54: /** terom@70: * Unload a module. This means calling its 'unload' func, which will then go about shutting down the module. This might terom@70: * not happen right away, though, so the module is not destroyed yet. terom@55: * terom@70: * XXX: currently the module is never destroyed, there needs to be a "module unload done" callback... terom@54: */ terom@54: err_t module_unload (struct module *module); terom@54: terom@70: /** terom@70: * Destroy a module, releasing as many resources as possible terom@70: */ terom@70: void module_destroy (struct module *module); terom@70: terom@70: /** terom@70: * Unload all modules, this just calls module_unload for each module, logging errors as warnings. terom@70: * terom@70: * XXX: currently, this does not destroy any modules, or the modules state itself. terom@70: */ terom@70: err_t modules_unload (struct modules *modules); terom@70: terom@54: #endif