# HG changeset patch # User Tero Marttila # Date 1237072642 -7200 # Node ID 6f7f6ae729d03304a71776afcf63a6914e59a090 # Parent 9f74e924b01a7b13e2e7a4e90cd8163d40292212 'working' modules code, and convert irc_log to use said interface, but we've hit the limits on our Makefile, and the compiled module doesn't really work diff -r 9f74e924b01a -r 6f7f6ae729d0 Makefile --- a/Makefile Fri Mar 13 17:38:23 2009 +0200 +++ b/Makefile Sun Mar 15 01:17:22 2009 +0200 @@ -34,25 +34,28 @@ # modules module_objs = $(patsubst src/%.c,obj/%.o,$(wildcard src/$(1)/*.c)) -CORE_OBJS = obj/error.o obj/log.o obj/chain.o obj/signals.o +CORE_OBJS = obj/error.o obj/log.o obj/chain.o SOCK_OBJS = obj/sock.o obj/sock_tcp.o SOCK_TEST_OBJS = obj/sock_test.o SOCK_GNUTLS_OBJS = obj/sock_gnutls.o LINEPROTO_OBJS = obj/line_proto.o IRC_OBJS = obj/irc_line.o obj/irc_conn.o obj/irc_net.o obj/irc_chan.o obj/irc_cmd.o obj/irc_proto.o obj/irc_client.o +NEXUS_OBJS = obj/signals.o obj/module.o IRC_LOG_OBJS = obj/irc_log.o # XXX: not yet there #CORE_OBJS = obj/lib/log.o obj/lib/signals.o # first target -all: ${BIN_PATHS} +all: ${BIN_PATHS} modules/irc_log.so # binaries -bin/nexus: ${CORE_OBJS} ${SOCK_OBJS} ${SOCK_GNUTLS_OBJS} ${LINEPROTO_OBJS} ${IRC_OBJS} ${IRC_LOG_OBJS} +bin/nexus: ${CORE_OBJS} ${SOCK_OBJS} ${SOCK_GNUTLS_OBJS} ${LINEPROTO_OBJS} ${IRC_OBJS} ${IRC_LOG_OBJS} ${NEXUS_OBJS} bin/test: ${CORE_OBJS} ${SOCK_OBJS} ${SOCK_GNUTLS_OBJS} ${SOCK_TEST_OBJS} ${LINEPROTO_OBJS} ${IRC_OBJS} +modules/irc_log.so: ${CORE_OBJS} ${IRC_OBJS} + # computed CFLAGS = ${MODE_CFLAGS} ${FIXED_CFLAGS} ${LIBEVENT_CFLAGS} ${GNUTLS_CFLAGS} ${EVSQL_CFLAGS} LDFLAGS = ${LIBEVENT_LDFLAGS} ${GNUTLS_LDFLAGS} ${EVSQL_LDFLAGS} @@ -89,11 +92,14 @@ # XXX: removed $(CPPFLAGS) obj/%.o : src/%.c - $(CC) -c $(CFLAGS) $< -o $@ + $(CC) -fpic -c $(CFLAGS) $< -o $@ bin/% : obj/%.o $(CC) $(LDFLAGS) $+ $(LOADLIBES) $(LDLIBS) -o $@ +modules/%.so : obj/%.o + $(CC) -shared -Wl,-soname,$(basename %@) -o $@ $+ -lc + # documentation DOXYGEN_PATH = /usr/bin/doxygen DOXYGEN_CONF_PATH = doc/doxygen.conf diff -r 9f74e924b01a -r 6f7f6ae729d0 src/error.c --- a/src/error.c Fri Mar 13 17:38:23 2009 +0200 +++ b/src/error.c Sun Mar 15 01:17:22 2009 +0200 @@ -4,6 +4,7 @@ // for the error_desc tables #include "sock.h" #include "sock_gnutls.h" +#include "module.h" #include #include @@ -46,6 +47,11 @@ { ERR_INVALID_NM, "Invalid nickmask", ERR_EXTRA_NONE }, { ERR_INVALID_NICK_LENGTH, "Nickname is too long", ERR_EXTRA_NONE }, { _ERR_INVALID, NULL, 0 } +}, _module_error_desc[] = { + { ERR_MODULE_OPEN, "module dlopen() failed", ERR_EXTRA_NONE }, + { ERR_MODULE_NAME, "invalid module name", ERR_EXTRA_NONE }, + { ERR_MODULE_INIT_FUNC, "invalid module init func", ERR_EXTRA_NONE }, + { _ERR_INVALID, NULL, 0 } }; /** @@ -56,6 +62,7 @@ _sock_error_desc, _sock_gnutls_error_desc, _irc_proto_error_desc, + _module_error_desc, NULL }; diff -r 9f74e924b01a -r 6f7f6ae729d0 src/error.h --- a/src/error.h Fri Mar 13 17:38:23 2009 +0200 +++ b/src/error.h Sun Mar 15 01:17:22 2009 +0200 @@ -77,6 +77,9 @@ /** irc_net errors */ _ERR_IRC_NET = 0x000900, ERR_IRC_NET_QUIT_STATE, + + /** modul errors */ + _ERR_MODULE = 0x000a00, }; /** diff -r 9f74e924b01a -r 6f7f6ae729d0 src/irc_client.c --- a/src/irc_client.c Fri Mar 13 17:38:23 2009 +0200 +++ b/src/irc_client.c Sun Mar 15 01:17:22 2009 +0200 @@ -70,6 +70,18 @@ return NULL; } +struct irc_chan* irc_client_get_chan (struct irc_client *client, const char *network, const char *channel) +{ + struct irc_net *net; + + // lookup network + if ((net = irc_client_get_net(client, network)) == NULL) + return NULL; + + // and then return channel lookup + return irc_net_get_chan(net, channel); +} + err_t irc_client_quit (struct irc_client *client, const char *message) { struct irc_net *net; diff -r 9f74e924b01a -r 6f7f6ae729d0 src/irc_client.h --- a/src/irc_client.h Fri Mar 13 17:38:23 2009 +0200 +++ b/src/irc_client.h Sun Mar 15 01:17:22 2009 +0200 @@ -44,6 +44,11 @@ struct irc_net* irc_client_get_net (struct irc_client *client, const char *network); /** + * Get an irc_chan by network/channel name + */ +struct irc_chan* irc_client_get_chan (struct irc_client *client, const char *network, const char *channel); + +/** * Quit cleanly from all our IRC networks. * * XXX: currently no way to indicate once we've quit all of them diff -r 9f74e924b01a -r 6f7f6ae729d0 src/irc_log.c --- a/src/irc_log.c Fri Mar 13 17:38:23 2009 +0200 +++ b/src/irc_log.c Sun Mar 15 01:17:22 2009 +0200 @@ -1,6 +1,8 @@ #include "irc_log.h" #include "log.h" +#include + // XXX: fix this err_t crap #define LIB_ERR_H #include @@ -28,33 +30,57 @@ .on_msg = on_chan_msg, }; -err_t irc_log_init (struct event_base *ev_base, const struct irc_log_info *info) +void* irc_log_init (struct modules *modules, struct error_info *err) { - struct irc_log_ctx *ctx = &_ctx; + struct irc_log_ctx *ctx; + + (void) modules; + (void) err; + + // XXX: static pointer + ctx = &_ctx; + + // ok + return ctx; +} + +err_t irc_log_conf (struct module *module, const char *name, char *value) +{ + struct irc_log_ctx *ctx = module->ctx; + struct nexus *nexus = module->modules->nexus; err_t err; - // open the database connection - if (info->db_info) { - log_info("connect to database: %s", info->db_info); + if (strcmp(name, "db_info") == 0) { + log_info("connect to database: %s", value); - if ((ctx->db = evsql_new_pq(ev_base, info->db_info, NULL, NULL)) == NULL) + if ((ctx->db = evsql_new_pq(nexus->ev_base, value, NULL, NULL)) == NULL) return ERR_EVSQL_NEW_PQ; + + } else if (strcmp(name, "channel") == 0) { + const char *network = strsep(&value, ":"); + const char *channel = value; + + struct irc_chan *chan; + + // kill missing tokens + if (!network || !channel) + // XXX: need to fix the error crap + return -1; + + // get the channel? + if ((chan = irc_client_get_chan(nexus->client, network, channel)) == NULL) + return -1; + + // add channel callbacks + if ((err = irc_chan_add_callbacks(chan, &_chan_callbacks, ctx))) + return err; + + } else { + return -1; + } - - if (info->channel) { - log_info("log channel: %s", irc_chan_name(info->channel)); - } - - // add channel callbacks - if ((err = irc_chan_add_callbacks(info->channel, &_chan_callbacks, ctx))) - goto error; // ok return SUCCESS; - -error: - // XXX: cleanup - - return err; } diff -r 9f74e924b01a -r 6f7f6ae729d0 src/irc_log.h --- a/src/irc_log.h Fri Mar 13 17:38:23 2009 +0200 +++ b/src/irc_log.h Sun Mar 15 01:17:22 2009 +0200 @@ -4,28 +4,24 @@ /** * @file * - * Logging IRC events to an SQL database + * Module for logging IRC events to an SQL database */ +#include "module.h" #include "error.h" #include "irc_chan.h" #include /** - * Configuration state for irc_log + * Initialize the irc_log module to use the given configuration */ -struct irc_log_info { - /** Database connection string */ - const char *db_info; - - /** The channel to log */ - struct irc_chan *channel; -}; +void* irc_log_init (struct modules *modules, struct error_info *err); /** - * Initialize the global irc_log module to use the given configuration + * Set one of the config options: * - * XXX: db_info is still unused if not specified + * db_info - the database connection string + * channel - the network:channel to log */ -err_t irc_log_init (struct event_base *ev_base, const struct irc_log_info *info); +err_t irc_log_conf (struct module *module, const char *name, char *value); #endif diff -r 9f74e924b01a -r 6f7f6ae729d0 src/module.c --- a/src/module.c Fri Mar 13 17:38:23 2009 +0200 +++ b/src/module.c Sun Mar 15 01:17:22 2009 +0200 @@ -1,26 +1,118 @@ #include "module.h" +#include "log.h" #include #include +#include +#include err_t modules_create (struct modules **modules_ptr, struct nexus *nexus) { - struct moduels *modules; + struct modules *modules; // alloc if ((modules = calloc(1, sizeof(*modules))) == NULL) return ERR_CALLOC; // init - TAILQ_INIT + TAILQ_INIT(&modules->list); // store modules->nexus = nexus; + + // ok + *modules_ptr = modules; + + return SUCCESS; } -err_t module_load (struct module **module_ptr, struct nexus *nexus, const struct module_info *info, struct error_info *err) +/** + * Load the symbol named "_", + */ +static err_t module_symbol (struct module *module, void **sym, const char *suffix) +{ + char sym_name[MODULE_SYMBOL_MAX]; + + // validate the length of the suffix + assert(strlen(suffix) <= MODULE_SUFFIX_MAX); + + // format + sprintf(sym_name, "%s_%s", module->info.name, suffix); + + // load + if ((*sym = dlsym(module->handle, sym_name)) == NULL) { + log_error("dlsym(%s, %s) failed: %s", module->info.name, sym_name, dlerror()); + + return ERR_MODULE_SYM; + } + + // ok + return SUCCESS; +} + +err_t module_load (struct modules *modules, struct module **module_ptr, const struct module_info *info, struct error_info *err) { struct module *module; + module_init_func_t init_func; + // validate the module name + if (strlen(info->name) > MODULE_NAME_MAX) + return SET_ERROR(err, ERR_MODULE_NAME); + + // alloc + if ((module = calloc(1, sizeof(*module))) == NULL) + return SET_ERROR(err, ERR_CALLOC); + + // store + module->info = *info; + module->modules = modules; + // clear dlerrors + (void) dlerror(); + + // load it + if ((module->handle = dlopen(info->path, RTLD_NOW)) == NULL) { + log_error("dlopen(%s) failed: %s", info->path, dlerror()); + + JUMP_SET_ERROR(err, ERR_MODULE_OPEN); + } + + // load the init symbol + if ((ERROR_CODE(err) = module_symbol(module, (void *) &init_func, "init"))) + JUMP_SET_ERROR(err, ERR_MODULE_INIT_FUNC); + + // call it + if ((module->ctx = init_func(modules, err))) + goto error; + + // add to modules list + TAILQ_INSERT_TAIL(&modules->list, module, modules_list); + + // ok + *module_ptr = module; + + return SUCCESS; + +error: + // XXX: cleanup + free(module); + + return ERROR_CODE(err); } + +err_t module_conf (struct module *module, const char *name, char *value) +{ + module_conf_func_t conf_func; + err_t err; + + // load the conf symbol + if ((err = module_symbol(module, (void *) &conf_func, "conf"))) + return err; + + // call it + if ((err = conf_func(module, name, value))) + return err; + + // ok + return SUCCESS; +} diff -r 9f74e924b01a -r 6f7f6ae729d0 src/module.h --- a/src/module.h Fri Mar 13 17:38:23 2009 +0200 +++ b/src/module.h Sun Mar 15 01:17:22 2009 +0200 @@ -4,7 +4,11 @@ /** * @file * - * Dynamically loadable modules for use with nexus + * Dynamically loadable modules for use with nexus. + * + * The modules are loaded using dlopen(), and hence should be standard dynamic libraries. Module initialization happens + * using a module_init_func_t named "_init", which should return some kind of context pointer, which can later be + * used to perform other operations on the module. */ #include "nexus.h" #include "error.h" @@ -29,11 +33,17 @@ /** The identifying info for the module */ struct module_info info; + /** The dlopen handle */ + void *handle; + /** The module context object */ void *ctx; + /** Pointer back to the modules struct used to load this */ + struct modules *modules; + /** Our entry in the list of modules */ - TAILQ_ENTRY(module) ctx_modules; + TAILQ_ENTRY(module) modules_list; }; /** @@ -44,13 +54,54 @@ struct nexus *nexus; /** List of loaded modules */ - TAILQ_HEAD(module_ctx_modules, module) 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 }; /** * Module initialization function type + * + * @param modules the module-loading context, containing the nexus + * @param err returned error info + * @return context pointer, or NULL on errors */ -typedef void* (*module_init_func_t) (struct nexus *nexus, struct error_info *err); +typedef void* (*module_init_func_t) (struct modules *modules, struct error_info *err); + +/** + * Module configuration function type + * + * @param ctx the module's context pointer + * @param name the name of the configuration setting + * @param value the value of the configuration setting + * @return error code + */ +typedef err_t (*module_conf_func_t) (struct module *module, const char *name, char *value); + +/** + * 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 @@ -58,12 +109,19 @@ err_t modules_create (struct modules **modules_ptr, struct nexus *nexus); /** - * Load a new module. + * Load a new module */ -err_t module_load (struct moduels *modules, struct module **module, const struct module_info *info, struct error_info *err); +err_t module_load (struct modules *modules, struct module **module, const struct module_info *info, struct error_info *err); + +/** + * Set a module configuration option + */ +err_t module_conf (struct module *module, const char *name, char *value); /** * Unload a module + * + * XXX: not implemented */ err_t module_unload (struct module *module); diff -r 9f74e924b01a -r 6f7f6ae729d0 src/nexus.c --- a/src/nexus.c Fri Mar 13 17:38:23 2009 +0200 +++ b/src/nexus.c Sun Mar 15 01:17:22 2009 +0200 @@ -44,7 +44,7 @@ void on_sigint (evutil_socket_t sig, short what, void *arg) { - struct nexus_ctx *ctx = arg; + struct nexus *ctx = arg; (void) sig; (void) what; @@ -73,12 +73,12 @@ { int opt, option_index; struct signals *signals; - struct nexus_ctx ctx; + struct nexus ctx; struct irc_net *net; struct error_info err; struct irc_net_info net_info = { - .network = NULL, + .network = "default", .hostname = DEFAULT_HOST, .service = DEFAULT_PORT, .use_ssl = false, @@ -89,15 +89,12 @@ } }; + // XXX: hardcode irc_log config + char *log_db_info = NULL; struct irc_chan_info log_chan_info = { .channel = NULL, }; - struct irc_log_info log_info = { - .db_info = NULL, - .channel = NULL, - }; - bool port_default = true; // parse options @@ -125,7 +122,7 @@ break; case OPT_LOG_DATABASE: - log_info.db_info = optarg; + log_db_info = optarg; break; case OPT_LOG_CHANNEL: @@ -149,6 +146,10 @@ // initialize sock module if (sock_init(ctx.ev_base, &err)) FATAL_ERROR(&err, "sock_init"); + + // modules + if ((ERROR_CODE(&err) = modules_create(&ctx.modules, &ctx))) + FATAL_ERROR(&err, "modules_create"); // the IRC client if (irc_client_create(&ctx.client, &err)) @@ -166,14 +167,33 @@ FATAL_ERROR(&err, "signals_add"); // logging? - if (log_info.db_info || log_chan_info.channel) { + if (log_db_info || log_chan_info.channel) { + struct module *mod_irc_log; + + struct module_info mod_irc_log_info = { + .name = "irc_log", + .path = "modules/irc_log.so" + }; + + // load the module + if (module_load(ctx.modules, &mod_irc_log, &mod_irc_log_info, &err)) + FATAL_ERROR(&err, "module_load"); + // get the channel - if (log_chan_info.channel && (log_info.channel = irc_net_add_chan(net, &log_chan_info)) == NULL) - FATAL("irc_net_add_chan"); - - // init the irc_log module - if ((ERROR_CODE(&err) = irc_log_init(ctx.ev_base, &log_info))) - FATAL_ERROR(&err, "irc_log_init"); + if (log_chan_info.channel) { + // create the channel + if ((irc_net_add_chan(net, &log_chan_info)) == NULL) + FATAL("irc_net_add_chan"); + + // configure it + // XXX: hardcoded + if ((ERROR_CODE(&err) = module_conf(mod_irc_log, "channel", "default/#test"))) + FATAL_ERROR(&err, "module_conf(irc_log, '%s', '%s)", "channel", "default/#test"); + } + + // configure the databse info + if (log_db_info && (ERROR_CODE(&err) = module_conf(mod_irc_log, "db_info", log_db_info))) + FATAL_ERROR(&err, "module_conf(irc_log, 'db_info', '%s')", log_db_info); } // run event loop diff -r 9f74e924b01a -r 6f7f6ae729d0 src/nexus.h --- a/src/nexus.h Fri Mar 13 17:38:23 2009 +0200 +++ b/src/nexus.h Sun Mar 15 01:17:22 2009 +0200 @@ -5,15 +5,19 @@ * A nexus is the central brain of the application; the place where the main() method is implemented */ #include +#include "module.h" #include "irc_client.h" /** * Context for async nexus operation */ -struct nexus_ctx { +struct nexus { /** The libevent base */ struct event_base *ev_base; + /** Our loaded modules */ + struct modules *modules; + /** The IRC client state */ struct irc_client *client; };