'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
--- 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
--- 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 <string.h>
#include <stdio.h>
@@ -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
};
--- 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,
};
/**
--- 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;
--- 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
--- 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 <string.h>
+
// XXX: fix this err_t crap
#define LIB_ERR_H
#include <evsql.h>
@@ -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;
}
--- 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 <event2/event.h>
/**
- * 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
--- 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 <stdlib.h>
#include <dlfcn.h>
+#include <string.h>
+#include <assert.h>
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 "<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(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;
+}
--- 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 "<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"
@@ -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);
--- 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
--- 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 <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;
};