src/module.h
author Tero Marttila <terom@fixme.fi>
Fri, 27 Mar 2009 17:22:43 +0200
changeset 83 c8e2dac08207
parent 70 a9a4c5e6aa30
child 87 f0db6ebf18b9
permissions -rw-r--r--
add config module and modify irc_log/nexus to use it
#ifndef MODULE_H
#define MODULE_H

/**
 * @file
 *
 * Dynamically loadable modules for use with nexus.
 *
 * The modules are loaded using dlopen(), and hence should be standard dynamic libraries. Modules are then "loaded" by
 * resolving a `struct module_funcs` symbol called '<modname>_funcs', and then using the init func to construct a
 * "context", which is then further manipulated by various other module functions.
 */
#include "nexus.h"
#include "config.h"
#include "error.h"

#include <sys/queue.h>

/**
 * Information required to load/identify a module.
 */
struct module_info {
    /** Human-readable name */
    const char *name;
    
    /** Filesystem path to the .so */
    const char *path;
};

/**
 * A module's behaviour is defined as a set of function pointers, which is dynamically resolved from the module DSO,
 * using the <mod_name>_funcs symbol.
 *
 * XXX: this also includes non-functions now...
 */
struct module_funcs {
    /**
     * Initialize the module, returning an opaque context pointer that is stored in the module state, and supplied for
     * subsequent calls. The supplied nexus arg can be used to access the global state.
     *
     * @param nexus a pointer to the nexus struct containing the global state
     * @param ctx_ptr the context pointer should be returned via this
     * @param err returned error info
     */
    err_t (*init) (struct nexus *nexus, void **ctx_ptr, struct error_info *err);

    /**
     * Set a configuration option with the given name and value, given by the user. Configuration settings are not
     * regarded as unique-per-name, but rather, several invocations of 'conf' with the same name and different values
     * could set up a multitude of things.
     *
     * The given value is either a NUL-terminated string value (which may be mutated, using e.g. strsep), or NULL if
     * no value was given (flag option).
     *
     * @param ctx the module's context pointer as returned by init
     * @param name the name of the configuration setting
     * @param value the value of the configuration setting
     * @param err returned error info
     */
    err_t (*conf) (void *ctx, const char *name, char *value, struct error_info *err);

    /**
     * Optionally, a module may also provide some kind of list of available configuration parameters, by setting this
     * to a pointer to an array of those.
     */
    const struct config_option *config_options;

    /**
     * Unload the module, removing all handlers/callbacks added to the nexus' irc_client. This does not have to act
     * immediately, and will still have access to all irc_* resources it had earlier, and may perform I/O to unload
     * cleanly. But the unloading should complete reasonably quickly, after which all event handlers added by the
     * module to the nexus' ev_base should have been removed, and resources released.
     *
     * @param ctx the module's context pointer as returned by init
     */
    err_t (*unload) (void *ctx);
};

/**
 * A loaded module.
 */
struct module {
    /** The identifying info for the module */
    struct module_info info;

    /** The dlopen handle */
    void *handle;

    /** The resolved function table */
    struct module_funcs *funcs;

    /** The module context object */
    void *ctx;

    /** Reference back to modules struct used to load this module */
    struct modules *modules;

    /** Our entry in the list of modules */
    TAILQ_ENTRY(module) modules_list;
};

/**
 * A set of loaded modules, and functionality to load more
 */
struct modules {
    /** The nexus in use */
    struct nexus *nexus;

    /** List of loaded modules */
    TAILQ_HEAD(module_ctx_modules, module) list;
};

/**
 * Possible error codes
 */
enum module_error_code {
    _ERR_MODULE_BEGIN = _ERR_MODULE,
    
    ERR_MODULE_OPEN,        ///< dlopen() failed
    ERR_MODULE_NAME,        ///< invalid module_info.name
    ERR_MODULE_SYM,         ///< invalid symbol
    ERR_MODULE_INIT_FUNC,   ///< invalid module_init_func_t
    ERR_MODULE_CONF,        ///< value error in configuration data
};


/**
 * Maximum length of a module name
 */
#define MODULE_NAME_MAX 24

/**
 * Maximum length of module symbol suffix
 */
#define MODULE_SUFFIX_MAX 16

/**
 * Maximum length of symbol name name, including terminating NUL
 */
#define MODULE_SYMBOL_MAX (MODULE_NAME_MAX + 1 + MODULE_SUFFIX_MAX + 1)

/**
 * Create a new modules state
 */
err_t modules_create (struct modules **modules_ptr, struct nexus *nexus);

/**
 * Load a new module
 *
 * @param modules the module-loading context
 * @param module_ptr return the new module via this, if not NULL
 * @param info the info required to identify and load the module
 * @param err return error info
 */
err_t module_load (struct modules *modules, struct module **module_ptr, const struct module_info *info, struct error_info *err);

/**
 * Lookup a module by name
 *
 * @param modules the modules state
 * @param name the module name to get
 * @return the module struct, or NULL if not found
 */
struct module* module_get (struct modules *modules, const char *name);

/**
 * Set a module configuration option
 */
err_t module_conf (struct module *module, const char *name, char *value, struct error_info *err);

/**
 * Unload a module. This means calling its 'unload' func, which will then go about shutting down the module. This might
 * not happen right away, though, so the module is not destroyed yet.
 *
 * XXX: currently the module is never destroyed, there needs to be a "module unload done" callback...
 */
err_t module_unload (struct module *module);

/**
 * Destroy a module, releasing as many resources as possible
 */
void module_destroy (struct module *module);

/**
 * Unload all modules, this just calls module_unload for each module, logging errors as warnings.
 *
 * XXX: currently, this does not destroy any modules, or the modules state itself.
 */
err_t modules_unload (struct modules *modules);

#endif