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@83: * The modules are loaded using dlopen(), and hence should be standard dynamic libraries. Modules are then "loaded" by terom@87: * resolving a `struct module_funcs` symbol called "_funcs", and then using the init func to construct a terom@83: * "context", which is then further manipulated by various other module functions. terom@110: * terom@110: * Modules are also reference counted, mainly for implementing module_unload(). When a module is first loaded, it has terom@110: * a reference count of one - the entry in struct modules. Later, modules can be referenced using module_get(), and terom@110: * returned with module_put() once done. The module should never be destroyed during its lifetime, until a terom@110: * module_unload() occurs. At this point, the module is removed from the modules_list, and the one reference is terom@110: * 'passed on' to the module itself. Once it has finished unloading, it can call module_unloaded() on the reference it was terom@110: * given by module_desc::unload, which may then result in a deferred module_destroy(). terom@110: * terom@110: * Module destroying itself is an interesting issue, since modules effectively need to be able to destroy themselves terom@110: * (as they must be able to perform cleanup, and then notify completion from inside an event loop callback). This means terom@110: * that they cannot directly execute a module_destroy() on themselves - if we call dlclose() with dlopen-mapped code terom@110: * pages on the stack, a segfault ensues. Hence, they must call module_unloaded() on themselves, which then executes a terom@110: * deferred module_destroy() if there are no references left. Otherwise, the module should be safe from external code, terom@110: * as module_put() should never cause a module to be destroyed before module_unloaded() is executed, due to the primary terom@110: * reference. terom@110: * terom@110: * Ugh, it's compliated, I need to write a clearer explenation once it's implemented :) terom@54: */ terom@110: terom@110: struct module; terom@110: terom@54: #include "nexus.h" terom@83: #include "config.h" terom@54: #include "error.h" terom@54: terom@54: #include terom@105: #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@100: * A table of (function/other) pointers defining a module's behaviour. This is dynamically resolved from the module DSO terom@100: * using the "_module" symbol. terom@57: */ terom@100: struct module_desc { 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@110: * Implementing this is mandatory. terom@110: * 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@100: * A module may define a set of available configuration parameters for use by module_conf, by setting this to an terom@100: * array of config_option's. terom@57: * terom@100: * The handler functions will recieve the module's context pointer as their ctx argument. terom@110: * terom@110: * Implementing this is optional, but recommended. terom@83: */ terom@83: const struct config_option *config_options; terom@83: terom@83: /** 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@110: * The module given as an argument is the module itself - which has been removed from the modules_list - the terom@110: * primary reference is passed on to the module. Once the module has finished unloading, it may call module_put(), terom@110: * which may then call module_destroy(), if no other references were left. terom@110: * terom@111: * If the unload operation fails (returns an error code), then the module is considered as unloaded (as if terom@111: * module_unloaded() was called - don't call this if you return an error). terom@111: * terom@119: * Implementing this is optional, if all of this can be implemented in destroy. terom@110: * terom@70: * @param ctx the module's context pointer as returned by init terom@111: * @param module the hanging module reference, that must be passed to module_unloaded() terom@111: * @return error code terom@70: */ terom@110: err_t (*unload) (void *ctx, struct module *module); terom@110: terom@110: /** terom@110: * Destroy the module now. No later chances, the module's code will be unloaded directly after this, which means terom@110: * that attempts to execute the module's code (even on the stack...) after this will segfault. terom@110: * terom@110: * The module code /should/ garuntee that this is never called from *inside* the module code - calls to terom@110: * module_destroy() will be deferred via the event loop if needed. terom@110: * terom@110: * Implementing this is optional. terom@110: */ terom@110: void (*destroy) (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@108: /** Possible dynamically allocated path */ terom@108: char *path_buf; terom@108: terom@55: /** The dlopen handle */ terom@55: void *handle; terom@55: terom@100: /** The module entry point */ terom@100: struct module_desc *desc; 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@110: /** Reference count for destroy() */ terom@110: size_t refcount; terom@110: terom@110: /** Is the module currently being unloaded? */ terom@100: bool unloading; terom@100: 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@108: /** Module search path */ terom@108: const char *path; terom@108: 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@108: ERR_MODULE_NAME, ///< invalid module_info.name terom@108: ERR_MODULE_DUP, ///< module already opened terom@108: ERR_MODULE_PATH, ///< resolving the path failed terom@55: ERR_MODULE_OPEN, ///< dlopen() failed 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@111: ERR_MODULE_STATE, ///< module in wrong state for operation 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@108: * Set a search path for finding modules by name. The given string won't be copied. terom@108: * terom@108: * A module called "" will be searched for at "/mod_.so" terom@108: * terom@108: * If path is NULL, this doesn't change anything. This returns the old path, which may be NULL. terom@108: * terom@108: * @param modules the modules state terom@108: * @param path the new search path, or NULL to just get the old one terom@108: * @return the old search path terom@108: */ terom@108: const char* modules_path (struct modules *modules, const char *path); terom@108: terom@108: /** terom@110: * Get a reference to the module, which must be returned using module_put terom@110: * terom@110: * @param modules the modules state terom@110: * @param name the module name to get terom@110: * @return the module struct, or NULL if not found terom@110: */ terom@134: struct module* modules_get (struct modules *modules, const char *name); terom@110: terom@110: /** terom@110: * Unload all modules, this just calls module_unload for each module, logging errors as warnings. terom@110: */ terom@110: err_t modules_unload (struct modules *modules); terom@110: terom@110: /** terom@110: * Destroy all modules immediately. terom@110: */ terom@110: void modules_destroy (struct modules *modules); terom@110: terom@110: terom@110: terom@110: /*******************************************/ terom@110: terom@110: terom@110: /** terom@103: * Return a module's name terom@103: */ terom@103: const char* module_name (struct module *module); terom@103: terom@103: /** terom@108: * Load a new module, as named by info. terom@108: * terom@108: * If info->path is not given, the module will be searched for using the path set by modules_path(). terom@65: * terom@111: * If module_ptr is given, a reference (that must be module_put'd) is returned, that must be returned using terom@111: * module_put(). terom@111: * terom@65: * @param modules the module-loading context terom@111: * @param module_ptr retuturned new module struct, as a new reference, if not NULL. terom@65: * @param info the info required to identify and load the module terom@111: * @param err returned 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@110: * Return a module retrieved using module_get terom@66: */ terom@110: void module_put (struct module *module); terom@66: terom@66: /** terom@100: * Look up a module configuration option by name terom@55: */ terom@100: const struct config_option* module_conf_lookup (struct module *module, const char *name, struct error_info *err); terom@100: terom@100: /** terom@100: * Apply a module configuration option using a structured value terom@100: */ terom@100: err_t module_conf (struct module *module, const struct config_option *opt, const struct config_value *value, struct error_info *err); terom@100: terom@100: /** terom@100: * Set a module configuration option using a raw value terom@100: */ terom@100: err_t module_conf_raw (struct module *module, const char *name, char *value, struct error_info *err); terom@54: terom@54: /** terom@110: * Unload a module. This removes the module from the modules_list, marks it as unloading, and then calls the module's terom@110: * \p unload function, passing it the primary reference. The module's unload code will then go about shutting down the terom@110: * module, and once that is done, it may module_put() the primary reference, which may then lead to module_destroy(). terom@111: * terom@111: * This returns ERR_MODULE_STATE if the module is already being unloaded, or other errors from the module's own unload terom@111: * functionality. terom@54: */ terom@54: err_t module_unload (struct module *module); terom@54: terom@70: /** terom@110: * Used by a module itself to indicate that an module_desc::unload() operation has completed. This will execute a terom@110: * deferred module_destroy() if there are no more references left on the module. terom@110: * terom@110: * Note: this is not intended to be called from outside the given module itself... terom@110: */ terom@110: void module_unloaded (struct module *module); terom@110: terom@110: /** terom@110: * Destroy a module, releasing as many resources as possible, but not stopping for errors. terom@110: * terom@110: * This does not enforce the correct refcount - 'tis the caller's responsibility. Prints out a warning if terom@110: * refcount > 0. terom@70: */ terom@70: void module_destroy (struct module *module); terom@70: terom@54: #endif