merge modules -> default, this is a bit early as it breaks functionality, but who cares, need to replace the build system now :)
authorTero Marttila <terom@fixme.fi>
Sun, 15 Mar 2009 23:24:21 +0200
changeset 58 65bd90f94f4e
parent 53 12d806823775 (current diff)
parent 57 ce1accba5fc7 (diff)
child 59 375a3b5c3a46
child 62 e0dabc496f64
merge modules -> default, this is a bit early as it breaks functionality, but who cares, need to replace the build system now :)
--- a/Makefile	Fri Mar 13 16:10:48 2009 +0200
+++ b/Makefile	Sun Mar 15 23:24:21 2009 +0200
@@ -34,28 +34,31 @@
 # 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
 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
+IRC_OBJS = obj/irc_line.o obj/irc_conn.o obj/irc_net.o obj/irc_chan.o obj/chain.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}
+
 # computed
-CFLAGS = ${MODE_CFLAGS} ${FIXED_CFLAGS} ${LIBEVENT_CFLAGS} ${GNUTLS_CFLAGS} ${EVSQL_CFLAGS}
-LDFLAGS = ${LIBEVENT_LDFLAGS} ${GNUTLS_LDFLAGS} ${EVSQL_LDFLAGS}
+CFLAGS = ${MODE_CFLAGS} ${FIXED_CFLAGS} ${LIBEVENT_CFLAGS} ${GNUTLS_CFLAGS} ${EVSQL_CFLAGS} -fpic 
+LDFLAGS = ${LIBEVENT_LDFLAGS} ${GNUTLS_LDFLAGS} ${EVSQL_LDFLAGS} -Wl,--export-dynamic
 
 # XXX: is this valid?
 CPPFLAGS = ${CFLAGS}
@@ -94,6 +97,9 @@
 bin/% : obj/%.o
 	$(CC) $(LDFLAGS) $+ $(LOADLIBES) $(LDLIBS) -o $@
 
+modules/%.so : obj/%.o
+	$(CC) -shared -Wl,-soname,$(notdir $@) -o $@ $+ -lc
+
 # documentation
 DOXYGEN_PATH = /usr/bin/doxygen
 DOXYGEN_CONF_PATH = doc/doxygen.conf
--- a/src/error.c	Fri Mar 13 16:10:48 2009 +0200
+++ b/src/error.c	Sun Mar 15 23:24:21 2009 +0200
@@ -4,6 +4,7 @@
 // for the error_desc tables
 #include "sock.h"
 #include "sock_gnutls.h"
+#include "module.h"
 
 #include <string.h>
 #include <stdio.h>
@@ -46,6 +47,12 @@
     {   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_STR       },
+    {   ERR_MODULE_NAME,                    "invalid module name",                      ERR_EXTRA_NONE      },
+    {   ERR_MODULE_INIT_FUNC,               "invalid module init func",                 ERR_EXTRA_STR       },
+    {   ERR_MODULE_CONF,                    "module_conf",                              ERR_EXTRA_STR       },
+    {   _ERR_INVALID,                       NULL,                                       0                   }
 };
 
 /**
@@ -56,6 +63,7 @@
     _sock_error_desc,
     _sock_gnutls_error_desc,
     _irc_proto_error_desc,
+    _module_error_desc,
     NULL
 };
 
@@ -125,6 +133,11 @@
                 snprintf(msg, ERROR_MSG_MAXLEN, "%s: %s", desc->name, gnutls_strerror(err->extra));
                 break;
             
+            case ERR_EXTRA_STR:
+                // static error message string
+                snprintf(msg, ERROR_MSG_MAXLEN, "%s: %s", desc->name, err->extra_str);
+                break;
+
             default:
                 // ???
                 snprintf(msg, ERROR_MSG_MAXLEN, "%s: %#.8x", desc->name, err->extra);
--- a/src/error.h	Fri Mar 13 16:10:48 2009 +0200
+++ b/src/error.h	Sun Mar 15 23:24:21 2009 +0200
@@ -29,6 +29,9 @@
 
     /** GnuTLS, using gnutls_strerror() */
     ERR_EXTRA_GNUTLS,
+
+    /** Static error message string */
+    ERR_EXTRA_STR,
 };
 
 /**
@@ -47,10 +50,10 @@
     ERR_GETADDRINFO,
     ERR_GETADDRINFO_EMPTY, 
     
-    /** @see enum sock_error_code*/
+    /** @see sock_error_code*/
     _ERR_SOCK       = 0x000300,
 
-    /** @see enum sock_gnutls_error_code */
+    /** @see sock_gnutls_error_code */
     _ERR_GNUTLS     = 0x000400,
 
     /** Libevent errors */
@@ -77,6 +80,9 @@
     /** irc_net errors */
     _ERR_IRC_NET    = 0x000900,
     ERR_IRC_NET_QUIT_STATE,
+
+    /** @see module_error_code */
+    _ERR_MODULE     = 0x000a00,
 };
 
 /**
@@ -99,9 +105,14 @@
 struct error_info {
     /** The base error code */
     err_t code;
+    
+    union {
+        /** Additional detail info, usually some third-party error code, as defined by the code's ERR_EXTRA_* */
+        int extra;
 
-    /** Additional detail info, usually some third-party error code, as defined by the code's ERR_EXTRA_* */
-    int extra;
+        /** Additional info, stored as a pointer to a static string (note how dangerous this is) */
+        const char *extra_str;
+    };
 };
 
 /**
@@ -150,6 +161,13 @@
 #define _SET_ERROR_ERRNO(err_info_ptr, err_code) _SET_ERROR_EXTRA(err_info_ptr, err_code, errno);
 #define SET_ERROR_ERRNO(err_info_ptr, err_code) SET_ERROR_EXTRA(err_info_ptr, err_code, errno);
 
+/** 
+ * Set error_info.code to err_code, and .extra_str to str. The given string pointer should remain valid while the error
+ * is being handled down-stack.
+ */
+#define _SET_ERROR_STR(err_info_ptr, err_code, err_str) (err_info_ptr)->code = (err_code); (err_info_ptr)->extra_str = (err_str)
+#define SET_ERROR_STR(err_info_ptr, err_code, err_str) do { _SET_ERROR_STR(err_info_ptr, err_code, err_str); } while(0)
+
 /** Set error_info from another error_info. Evaluates to the new error_info */
 #define SET_ERROR_INFO(err_info_ptr, from_ptr) (*err_info_ptr = *from_ptr)
 
@@ -158,9 +176,11 @@
 #define RETURN_SET_ERROR_EXTRA(err_info_ptr, err_code, err_extra) do { _SET_ERROR_EXTRA(err_info_ptr, err_code, err_extra); return (err_code); } while (0)
 #define RETURN_SET_ERROR_ERRNO(err_info_ptr, err_code) do { _SET_ERROR_ERRNO(err_info_ptr, err_code); return (err_code); } while (0)
 #define RETURN_SET_ERROR_INFO(err_info_ptr, from_ptr) do { SET_ERROR_INFO(err_info_ptr, from_ptr); return (from_ptr->code); } while (0)
+#define RETURN_SET_ERROR_STR(err_info_ptr, err_code, err_str) do { _SET_ERROR_STR(err_info_ptr, err_code, err_str); return (err_code); } while (0)
 
 /** Same as above, but also do a 'goto error' */
 #define JUMP_SET_ERROR(err_info_ptr, err_code) do { SET_ERROR(err_info_ptr, err_code); goto error; } while (0)
 #define JUMP_SET_ERROR_INFO(err_info_ptr, from_ptr) do { SET_ERROR_INFO(err_info_ptr, from_ptr); goto error; } while (0)
+#define JUMP_SET_ERROR_STR(err_info_ptr, err_code, err_str) do { _SET_ERROR_STR(err_info_ptr, err_code, err_str); goto error; } while (0)
 
 #endif
--- a/src/irc_client.c	Fri Mar 13 16:10:48 2009 +0200
+++ b/src/irc_client.c	Sun Mar 15 23:24:21 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;
--- a/src/irc_client.h	Fri Mar 13 16:10:48 2009 +0200
+++ b/src/irc_client.h	Sun Mar 15 23:24:21 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
--- a/src/irc_log.c	Fri Mar 13 16:10:48 2009 +0200
+++ b/src/irc_log.c	Sun Mar 15 23:24:21 2009 +0200
@@ -1,18 +1,27 @@
-#include "irc_log.h"
+#include "module.h"
+#include "irc_chan.h"
+#include "error.h"
 #include "log.h"
 
+#include <stdlib.h>
+#include <string.h>
+
+#include <event2/event.h>
 // XXX: fix this err_t crap
 #define LIB_ERR_H
 #include <evsql.h>
 
 /**
- * The core irc_log state
+ * The irc_log module state
  */
-static struct irc_log_ctx {
+struct irc_log_ctx {
+    /** The nexus this module is loaded for */
+    struct nexus *nexus;
+
     /** The database connection */
     struct evsql *db;
 
-} _ctx;
+};
 
 static void on_chan_msg (struct irc_chan *chan, const struct irc_nm *source, const char *message, void *arg)
 {
@@ -28,33 +37,67 @@
     .on_msg         = on_chan_msg,
 };
 
-err_t irc_log_init (struct event_base *ev_base, const struct irc_log_info *info)
+static err_t irc_log_init (struct nexus *nexus, void **ctx_ptr, struct error_info *err)
 {
-    struct irc_log_ctx *ctx = &_ctx;
-    err_t err;
-
-    // open the database connection
-    if (info->db_info) {
-        log_info("connect to database: %s", info->db_info);
+    struct irc_log_ctx *ctx;
+    
+    // allocate
+    if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
+        return SET_ERROR(err, ERR_CALLOC);
 
-        if ((ctx->db = evsql_new_pq(ev_base, info->db_info, NULL, NULL)) == NULL)
+    // initialize
+    memset(ctx, 0, sizeof(*ctx));
+
+    // store
+    ctx->nexus = nexus;
+
+    // ok
+    *ctx_ptr = ctx;
+
+    return SET_ERROR(err, SUCCESS);
+}
+
+static err_t irc_log_conf (void *mod_ctx, const char *name, char *value, struct error_info *err)
+{
+    struct irc_log_ctx *ctx = mod_ctx;
+
+    if (strcmp(name, "db_info") == 0) {
+        log_info("connect to database: %s", value);
+
+        if ((ctx->db = evsql_new_pq(ctx->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) 
+            RETURN_SET_ERROR_STR(err, ERR_MODULE_CONF, "invalid '<network>/<channel>' value");
+
+        // get the channel?
+        if ((chan = irc_client_get_chan(ctx->nexus->client, network, channel)) == NULL)
+            RETURN_SET_ERROR_STR(err, ERR_MODULE_CONF, "unknown channel name");
+
+        // add channel callbacks
+        if ((ERROR_CODE(err) = irc_chan_add_callbacks(chan, &_chan_callbacks, ctx)))
+            return ERROR_CODE(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;
 }
 
+/**
+ * The module function table
+ */
+struct module_funcs irc_log_funcs = {
+    .init       = &irc_log_init,
+    .conf       = &irc_log_conf,
+};
--- a/src/irc_log.h	Fri Mar 13 16:10:48 2009 +0200
+++ b/src/irc_log.h	Sun Mar 15 23:24:21 2009 +0200
@@ -1,31 +1,1 @@
-#ifndef IRC_LOG_H
-#define IRC_LOG_H
-
-/**
- * @file
- *
- * Logging IRC events to an SQL database
- */
-#include "error.h"
-#include "irc_chan.h"
-#include <event2/event.h>
 
-/**
- * Configuration state for irc_log
- */
-struct irc_log_info {
-    /** Database connection string */
-    const char *db_info;
-
-    /** The channel to log */
-    struct irc_chan *channel;
-};
-
-/**
- * Initialize the global irc_log module to use the given configuration
- *
- * XXX: db_info is still unused if not specified
- */
-err_t irc_log_init (struct event_base *ev_base, const struct irc_log_info *info);
-
-#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/module.c	Sun Mar 15 23:24:21 2009 +0200
@@ -0,0 +1,100 @@
+#include "module.h"
+#include "log.h"
+
+#include <stdlib.h>
+#include <dlfcn.h>
+#include <string.h>
+#include <assert.h>
+
+err_t modules_create (struct modules **modules_ptr, struct nexus *nexus)
+{
+    struct modules *modules;
+
+    // alloc
+    if ((modules = calloc(1, sizeof(*modules))) == NULL)
+        return ERR_CALLOC;
+    
+    // init
+    TAILQ_INIT(&modules->list);
+
+    // store
+    modules->nexus = nexus;
+
+    // ok
+    *modules_ptr = modules;
+
+    return SUCCESS;
+}
+
+/**
+ * Load the symbol named "<module>_<suffix>".
+ */ 
+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(module->info.name) <= MODULE_NAME_MAX);
+    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)
+       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;
+
+    // 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;
+
+    // clear dlerrors
+    (void) dlerror();
+
+    // load it
+    if ((module->handle = dlopen(info->path, RTLD_NOW)) == NULL)
+        JUMP_SET_ERROR_STR(err, ERR_MODULE_OPEN, dlerror());
+    
+    // load the funcs symbol
+    if ((ERROR_CODE(err) = module_symbol(module, (void **) &module->funcs, "funcs")))
+        JUMP_SET_ERROR_STR(err, ERROR_CODE(err), dlerror());
+
+    // call the init func
+    if ((module->funcs->init(modules->nexus, &module->ctx, 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, struct error_info *err)
+{
+    // call the conf func
+    return module->funcs->conf(module->ctx, name, value, err);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/module.h	Sun Mar 15 23:24:21 2009 +0200
@@ -0,0 +1,141 @@
+#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. Module initialization happens
+ * using a module_init_func_t named "<name>_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"
+
+#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.
+ */
+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.
+     *
+     * XXX: make value a non-modifyable string
+     *
+     * @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);
+};
+
+/**
+ * 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;
+
+    /** 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
+ */
+err_t module_load (struct modules *modules, struct module **module_ptr, 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, struct error_info *err);
+
+/**
+ * Unload a module
+ *
+ * XXX: not implemented
+ */
+err_t module_unload (struct module *module);
+
+#endif
--- a/src/nexus.c	Fri Mar 13 16:10:48 2009 +0200
+++ b/src/nexus.c	Sun Mar 15 23:24:21 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,35 @@
         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) {
+            char conf_channel[] = "default/#test";
+
+            // create the channel
+            if ((irc_net_add_chan(net, &log_chan_info)) == NULL)
+                FATAL("irc_net_add_chan");
+
+            // configure it
+            // XXX: hardcoded
+            if (module_conf(mod_irc_log, "channel", conf_channel, &err))
+                FATAL_ERROR(&err, "module_conf(irc_log, '%s', '%s)", "channel", conf_channel);
+        }
+
+        // configure the databse info
+        if (log_db_info && module_conf(mod_irc_log, "db_info", log_db_info, &err))
+            FATAL_ERROR(&err, "module_conf(irc_log, 'db_info', '%s')", log_db_info);
     }
 
     // run event loop
--- a/src/nexus.h	Fri Mar 13 16:10:48 2009 +0200
+++ b/src/nexus.h	Sun Mar 15 23:24:21 2009 +0200
@@ -4,16 +4,23 @@
 /**
  * A nexus is the central brain of the application; the place where the main() method is implemented
  */
+
+struct nexus;
+
 #include <event2/event.h>
+#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;
 };