--- a/src/CMakeLists.txt Fri Mar 27 01:01:34 2009 +0200
+++ b/src/CMakeLists.txt Fri Mar 27 17:22:43 2009 +0200
@@ -12,7 +12,7 @@
set (SOCK_SOURCES sock.c sock_tcp.c sock_gnutls.c sock_test.c line_proto.c)
set (IRC_SOURCES irc_line.c irc_conn.c irc_net.c irc_chan.c chain.c irc_cmd.c irc_proto.c irc_client.c irc_user.c)
-set (NEXUS_SOURCES nexus.c ${CORE_SOURCES} ${SOCK_SOURCES} ${IRC_SOURCES} signals.c module.c)
+set (NEXUS_SOURCES nexus.c ${CORE_SOURCES} ${SOCK_SOURCES} ${IRC_SOURCES} signals.c module.c config.c)
set (TEST_SOURCES test.c ${CORE_SOURCES} ${SOCK_SOURCES} ${IRC_SOURCES})
set (IRC_LOG_SOURCES irc_log.c)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/config.c Fri Mar 27 17:22:43 2009 +0200
@@ -0,0 +1,21 @@
+#include "config.h"
+
+#include <string.h>
+
+err_t config_apply (struct config_option *options, void *ctx, const char *name, char *value, struct error_info *err)
+{
+ struct config_option *option;
+
+ // find the matching config opt
+ for (option = options; option->name && option->type; option++) {
+ if (strcmp(option->name, name) == 0)
+ break;
+ }
+
+ // no matching option found?
+ if (!option)
+ RETURN_SET_ERROR_STR(err, ERR_CONFIG_NAME, name);
+
+ // call the handler
+ return option->func(ctx, value, err);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/config.h Fri Mar 27 17:22:43 2009 +0200
@@ -0,0 +1,43 @@
+#ifndef CONFIG_H
+#define CONFIG_H
+
+/**
+ * Support for module configuration parameters
+ */
+#include "error.h"
+
+/**
+ * Different types of configuration parameters
+ */
+enum config_type {
+ CONFIG_INVALID,
+
+ /** A plain NUL-terminated string */
+ CONFIG_STRING,
+};
+
+struct config_option {
+ /** The name of the config option */
+ const char *name;
+
+ /** The type of the value, XXX: unused */
+ enum config_type type;
+
+ /** The value handler func */
+ err_t (*func) (void *ctx, char *value, struct error_info *err);
+
+ /** The value description */
+ const char *description;
+
+ /** Help text */
+ const char *help;
+};
+
+/**
+ * Apply a configuration name/value to an array of config_option's.
+ *
+ * This finds the appropriate config_option and calls its func.
+ */
+err_t config_apply (struct config_option *options, void *ctx, const char *name, char *value, struct error_info *err);
+
+#endif
--- a/src/error.c Fri Mar 27 01:01:34 2009 +0200
+++ b/src/error.c Fri Mar 27 17:22:43 2009 +0200
@@ -19,6 +19,7 @@
{ ERR_EVENT_ADD, "event_add", ERR_EXTRA_NONE },
{ ERR_EVSQL_NEW_PQ, "evsql_new_pq", ERR_EXTRA_NONE },
{ ERR_EVSQL_QUERY_EXEC, "evsql_query_exec", ERR_EXTRA_NONE },
+ { ERR_CONFIG_NAME, "no matching config name", ERR_EXTRA_STR },
{ ERR_CMD_OPT, "argv", ERR_EXTRA_STR },
{ _ERR_INVALID, NULL, 0 }
--- a/src/error.h Fri Mar 27 01:01:34 2009 +0200
+++ b/src/error.h Fri Mar 27 17:22:43 2009 +0200
@@ -86,9 +86,14 @@
/** @see module_error_code */
_ERR_MODULE = 0x000a00,
+ /** config errors */
+ _ERR_CONFIG = 0x000b00,
+ ERR_CONFIG_NAME,
+
/** General errors */
_ERR_GENERAL = 0xffff00,
ERR_CMD_OPT,
+
};
/**
--- a/src/irc_chan.c Fri Mar 27 01:01:34 2009 +0200
+++ b/src/irc_chan.c Fri Mar 27 17:22:43 2009 +0200
@@ -158,7 +158,6 @@
strcpy(names, arg_names);
// iterate over each name
- // XXX: nickflags
while ((nickname = strsep(&names, " "))) {
// skip empty token at end
if (strlen(nickname) == 0)
--- a/src/irc_log.c Fri Mar 27 01:01:34 2009 +0200
+++ b/src/irc_log.c Fri Mar 27 17:22:43 2009 +0200
@@ -1,5 +1,6 @@
#include "module.h"
#include "irc_chan.h"
+#include "config.h"
#include "error.h"
#include "log.h"
@@ -398,8 +399,10 @@
*
* Fails if ctx->db is already set.
*/
-static err_t irc_log_conf_db_info (struct irc_log_ctx *ctx, char *value, struct error_info *err)
+static err_t irc_log_conf_db_info (void *_ctx, char *value, struct error_info *err)
{
+ struct irc_log_ctx *ctx = _ctx;
+
log_info("connect to database: %s", value);
// already connected?
@@ -422,8 +425,9 @@
* Fails if the value is invalid, we don't have a database connected, the channel doesn't exist, adding our command
* handlers/callbacks fails, or sending the initial INSERT-OPEN query fails.
*/
-static err_t irc_log_conf_channel (struct irc_log_ctx *ctx, char *value, struct error_info *err)
+static err_t irc_log_conf_channel (void *_ctx, char *value, struct error_info *err)
{
+ struct irc_log_ctx *ctx = _ctx;
const char *network, *channel;
struct irc_chan *chan;
@@ -455,6 +459,15 @@
}
/**
+ * Our configuration options
+ */
+struct config_option irc_log_config_options[] = {
+ { "db_info", CONFIG_STRING, &irc_log_conf_db_info, "[<key>=<value> [...]]", "set database connection info, see libpq docs" },
+ { "channel", CONFIG_STRING, &irc_log_conf_channel, "<network>:<channel>", "log the given channel" },
+ { NULL, 0, NULL, NULL, NULL }
+};
+
+/**
* Set a config option
*/
static err_t irc_log_conf (void *_ctx, const char *name, char *value, struct error_info *err)
@@ -465,19 +478,8 @@
if (ctx->unloading)
RETURN_SET_ERROR_STR(err, ERR_MODULE_CONF, "module is being unloaded");
- // apply the config setting
- if (strcmp(name, "db_info") == 0) {
- return irc_log_conf_db_info(ctx, value, err);
-
- } else if (strcmp(name, "channel") == 0) {
- return irc_log_conf_channel(ctx, value, err);
-
- } else {
- RETURN_SET_ERROR_STR(err, ERR_MODULE_CONF, "unknown configuration option");
- }
-
- // ok
- return SUCCESS;
+ // apply it
+ return config_apply(irc_log_config_options, ctx, name, value, err);
}
/**
@@ -504,8 +506,9 @@
* The module function table
*/
struct module_funcs irc_log_funcs = {
- .init = &irc_log_init,
- .conf = &irc_log_conf,
- .unload = &irc_log_unload,
+ .init = &irc_log_init,
+ .conf = &irc_log_conf,
+ .config_options = irc_log_config_options,
+ .unload = &irc_log_unload,
};
--- a/src/module.h Fri Mar 27 01:01:34 2009 +0200
+++ b/src/module.h Fri Mar 27 17:22:43 2009 +0200
@@ -6,11 +6,12 @@
*
* 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.
+ * 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>
@@ -29,6 +30,8 @@
/**
* 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 {
/**
@@ -57,6 +60,12 @@
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
--- a/src/nexus.c Fri Mar 27 01:01:34 2009 +0200
+++ b/src/nexus.c Fri Mar 27 17:22:43 2009 +0200
@@ -43,9 +43,11 @@
};
/**
- * Display --help output on stdout
+ * Display --help output on stdout.
+ *
+ * If nexus is given, --config options for loaded modules are also listed
*/
-static void usage (const char *exe)
+static void usage (struct nexus *nexus, const char *exe)
{
printf("Usage: %s [OPTIONS]\n", exe);
printf("\n");
@@ -55,6 +57,29 @@
printf(" --module / -m add a module using '<name>:<path>' format\n");
printf(" --config / -C add a module configuration option using '<mod_name>:<name>[:<value>]' format\n");
printf(" --debug / -d set logging level to DEBUG\n");
+
+ if (nexus && !TAILQ_EMPTY(&nexus->modules->list)) {
+ struct module *module;
+
+ printf("\n");
+ printf("Module configuration options\n");
+
+ TAILQ_FOREACH(module, &nexus->modules->list, modules_list) {
+ printf("\n");
+ printf("%s:\n", module->info.name);
+
+ if (module->funcs->config_options) {
+ const struct config_option *opt;
+
+ for (opt = module->funcs->config_options; opt->name && opt->type; opt++) {
+ printf(" --config %s:%s:%s\t\t%s\n", module->info.name, opt->name, opt->description, opt->help);
+ }
+
+ } else {
+ printf("\t???\n");
+ }
+ }
+ }
}
/**
@@ -218,7 +243,7 @@
while ((opt = getopt_long(argc, argv, "hn:c:m:C:", options, &option_index)) != -1) {
switch (opt) {
case OPT_HELP:
- usage(argv[0]);
+ usage(nexus, argv[0]);
// XXX: return instead
exit(EXIT_SUCCESS);
@@ -252,7 +277,7 @@
break;
case '?':
- usage(argv[0]);
+ usage(nexus, argv[0]);
return SET_ERROR(err, ERR_CMD_OPT);
}
}