--- a/src/CMakeLists.txt Wed May 27 23:07:00 2009 +0300
+++ b/src/CMakeLists.txt Wed May 27 23:57:48 2009 +0300
@@ -9,15 +9,20 @@
# add our include path
include_directories (${LibEvent_INCLUDE_DIRS} ${GnuTLS_INCLUDE_DIRS} ${Evsql_INCLUDE_DIRS} ${Lua51_INCLUDE_DIRS} ${PCRE_INCLUDE_DIRS})
+# XXX: hack until we separate these
+include_directories (${CMAKE_CURRENT_SOURCE_DIR})
+
# define our source code modules
-set (CORE_SOURCES error.c log.c str.c object.c)
+set (LIB_SOURCES lib/error.c lib/log.c lib/str.c lib/object.c)
set (IO_SOURCES transport.c service.c transport_fd.c sock.c resolve.c tcp.c tcp_transport.c tcp_client.c tcp_server.c ssl.c ssl_client.c fifo.c)
set (PROTO_SOURCES line_proto.c msg_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 irc_queue.c irc_net_connect.c)
set (LUA_SOURCES nexus_lua.c lua_objs.c lua_config.c lua_irc.c lua_func.c lua_type.c lua_thread.c)
set (CONSOLE_SOURCES console.c lua_console.c)
-set (NEXUS_SOURCES nexus.c ${CORE_SOURCES} ${IO_SOURCES} ${PROTO_SOURCES} ${IRC_SOURCES} ${LUA_SOURCES} ${CONSOLE_SOURCES} signals.c module.c config.c)
+set (SPBOT_SOURCES spbot/nexus.c spbot/signals.c spbot/module.c spbot/config.c)
+
+set (NEXUS_SOURCES ${SPBOT_SOURCES} ${LIB_SOURCES} ${IO_SOURCES} ${PROTO_SOURCES} ${IRC_SOURCES} ${LUA_SOURCES} ${CONSOLE_SOURCES})
set (IRC_LOG_SOURCES modules/irc_log.c)
set (LOGWATCH_SOURCES modules/logwatch.c modules/logwatch_source.c modules/logwatch_filter.c modules/logwatch_chan.c)
--- a/src/config.c Wed May 27 23:07:00 2009 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,260 +0,0 @@
-#include "config.h"
-
-#include <string.h>
-#include <assert.h>
-
-const struct config_option* config_lookup (const struct config_option *options, const char *name, struct error_info *err)
-{
- const struct config_option *option;
-
- // find the matching config opt
- for (option = options; option->name; option++) {
- if (strcmp(option->name, name) == 0)
- return option;
- }
-
- // not found
- SET_ERROR_STR(err, ERR_MODULE_CONF, "unknown configuration option");
-
- return NULL;
-}
-
-int config_params_count (const struct config_option *option)
-{
- const struct config_param *param;
- int count = 0;
-
- // handle each param
- for (param = option->params; param->name && param->type; param++)
- count++;
-
- return count;
-}
-
-/**
- * Parse a raw value for a CONFIG_IRC_CHAN into an irc_chan
- */
-static struct irc_chan* config_parse_irc_chan (struct nexus *nexus, char *raw_value, struct error_info *err)
-{
- const char *network, *channel;
- struct irc_chan *chan;
-
- // XXX: wrong error code
-
- // parse required args
- if ((network = strsep(&raw_value, "/")) == NULL)
- JUMP_SET_ERROR_STR(err, ERR_CONFIG_VALUE, "invalid <network> for CONFIG_IRC_CHAN value");
-
- if ((channel = strsep(&raw_value, "/")) == NULL)
- JUMP_SET_ERROR_STR(err, ERR_CONFIG_VALUE, "invalid <channel> for CONFIG_IRC_CHAN value");
-
- // extraneous stuff?
- if (raw_value)
- JUMP_SET_ERROR_STR(err, ERR_CONFIG_VALUE, "trailing data for CONFIG_IRC_CHAN value");
-
- // get the channel?
- if ((chan = irc_client_get_chan(nexus->client, network, channel)) == NULL)
- JUMP_SET_ERROR_STR(err, ERR_CONFIG_VALUE, "unknown network/channel name for CONFIG_IRC_CHAN value");
-
- // ok
- return chan;
-
-error:
- return NULL;
-}
-
-err_t config_parse_param (const struct config_param *param, struct nexus *nexus, struct config_value *value, char *raw_value, struct error_info *err)
-{
- // parse the value
- switch (param->type) {
- case CONFIG_INVALID:
- RETURN_SET_ERROR_STR(err, ERR_CONFIG_TYPE, "invalid value for invalid type (too many values?)");
-
- case CONFIG_STRING:
- // simple!
- value->string = raw_value;
- break;
-
- case CONFIG_IRC_CHAN:
- // parse the value
- if (!(value->irc_chan = config_parse_irc_chan(nexus, raw_value, err)))
- return ERROR_CODE(err);
-
- break;
-
- case CONFIG_USER:
- // fail
- RETURN_SET_ERROR_STR(err, ERR_CONFIG_TYPE, "user type can't be parsed");
-
- default:
- NOT_REACHED();
- }
-
- // copy the type
- value->type = param->type;
-
- // ok
- return SUCCESS;
-}
-
-err_t config_parse (const struct config_option *option, struct nexus *nexus, struct config_value *value, char *raw_value, struct error_info *err)
-{
- // use the first param
- const struct config_param *param = &option->params[0];
-
- // must have exactly one param
- if (!param->type || (param + 1)->type)
- return SET_ERROR(err, ERR_CONFIG_PARAMS);
-
- // parse it
- return config_parse_param(param, nexus, value, raw_value, err);
-}
-
-err_t config_apply_opt (const struct config_option *option, void *ctx, const struct config_value values[], struct error_info *err)
-{
- const struct config_param *param;
- const struct config_value *value;
-
- // handle each param
- for (param = option->params, value = values; param->name && param->type; param++) {
- // no value given, or value given as NULL?
- if (!value->type || value->type == CONFIG_NULL) {
- if (param->optional) {
- // optional, so just ignore the value
-
- } else {
- // required
- JUMP_SET_ERROR_STR(err, ERR_CONFIG_REQUIRED, param->name);
-
- }
-
- } else if (param->type != value->type) {
- // invalid type, XXX: also report correct type name?
- JUMP_SET_ERROR(err, ERR_CONFIG_TYPE);
-
- } else if (param->is_handler) {
- // only applicable for non-optional args
- err_t tmp;
-
- // invoke the handler
- switch (param->type) {
- case CONFIG_STRING: tmp = param->func.string(ctx, value->string, err); break;
- case CONFIG_IRC_CHAN: tmp = param->func.irc_chan(ctx, value->irc_chan, err); break;
- default: NOT_REACHED();
- }
-
- // abort on errors
- if (tmp)
- goto error;
- }
-
- // the values list is NULL-terminated, and optional params can be left off
- if (value->type)
- value++;
- }
-
- // the option's handler?
- if (option->func && option->func(ctx, option, values, err))
- goto error;
-
- // ok
- return SUCCESS;
-
-error:
- return ERROR_CODE(err);
-}
-
-err_t config_apply (const struct config_option *options, void *ctx, const char *name, const struct config_value values[], struct error_info *err)
-{
- const struct config_option *option;
-
- // no matching option found?
- if (!(option = config_lookup(options, name, err)))
- return ERROR_CODE(err);
-
- // apply it
- return config_apply_opt(option, ctx, values, err);
-}
-
-err_t config_apply_string (const struct config_option *options, void *ctx, const char *name, char *value, struct error_info *err)
-{
- struct config_value conf_value[] = {
- CONFIG_VALUE_STRING(value),
- CONFIG_VALUE_END,
- };
-
- return config_apply(options, ctx, name, conf_value, err);
-}
-
-err_t config_apply_irc_chan (const struct config_option *options, void *ctx, const char *name, struct irc_chan *value, struct error_info *err)
-{
- struct config_value conf_value[] = {
- CONFIG_VALUE_IRC_CHAN(value),
- CONFIG_VALUE_END,
- };
-
- return config_apply(options, ctx, name, conf_value, err);
-}
-
-err_t config_apply_raw (const struct config_option options[], struct nexus *nexus, void *ctx, const char *name, char *raw_value, struct error_info *err)
-{
- const struct config_option *option;
- struct config_value value[2] = {
- CONFIG_VALUE_END,
- CONFIG_VALUE_END,
- };
-
- // no matching option found?
- if (!(option = config_lookup(options, name, err)))
- return ERROR_CODE(err);
-
- // parse it
- if (config_parse(option, nexus, &value[0], raw_value, err))
- return ERROR_CODE(err);
-
- // apply it
- return config_apply_opt(option, ctx, value, err);
-}
-
-const struct config_value* config_get_value (const struct config_option *option, const struct config_value values[], const char *name)
-{
- const struct config_param *param;
- const struct config_value *value;
-
- // go through the (param, value) pairs
- for (param = option->params, value = values; param->name && param->type && value->type; param++, value++) {
- if (strcmp(param->name, name) == 0) {
- // not NULL?
- if (value->type != CONFIG_NULL)
- return value;
-
- else
- return NULL;
- }
- }
-
- // not found
- return NULL;
-}
-
-const char* config_get_string (const struct config_option *option, const struct config_value values[], const char *name)
-{
- const struct config_value *value;
-
- return (value = config_get_value(option, values, name)) ? value->string : NULL;
-}
-
-struct irc_chan* config_get_irc_chan (const struct config_option *option, const struct config_value values[], const char *name)
-{
- const struct config_value *value;
-
- return (value = config_get_value(option, values, name)) ? value->irc_chan : NULL;
-}
-
-void* config_get_user (const struct config_option *option, const struct config_value values[], const char *name, const struct config_user_type *user_type)
-{
- const struct config_value *value;
-
- return ((value = config_get_value(option, values, name)) && value->user.type == user_type) ? value->user.ptr : NULL;
-}
-
--- a/src/config.h Wed May 27 23:07:00 2009 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,281 +0,0 @@
-#ifndef CONFIG_H
-#define CONFIG_H
-
-/**
- * @file
- *
- * Support for module configuration parameters
- */
-#include "error.h"
-#include <stdbool.h>
-
-/**
- * Different types of configuration parameters
- */
-enum config_type {
- CONFIG_INVALID,
-
- /** No value at all */
- CONFIG_NULL,
-
- /** A plain NUL-terminated string */
- CONFIG_STRING,
-
- /** A user-defined type, further identified by a string */
- CONFIG_USER,
-
- /** An IRC channel */
- CONFIG_IRC_CHAN,
-};
-
-/**
- * A CONFIG_USER type info
- */
-struct config_user_type {
- /** The name of the type */
- const char *name;
-};
-
-/**
- * Structure to hold a value as defined by config_type
- */
-struct config_value {
- /** The type of the value */
- enum config_type type;
-
- /** The typed value */
- union {
- /** Value for CONFIG_STRING */
- char *string;
-
- /** Value for CONFIG_IRC_CHAN */
- struct irc_chan *irc_chan;
-
- /** Value for CONFIG_USER */
- struct {
- /** The specific user type */
- const struct config_user_type *type;
-
- /** The pointer value */
- void *ptr;
- } user;
- };
-};
-
-/**
- * Structure to define a single parameter for an option
- */
-struct config_param {
- /** The name of the arg */
- const char *name;
-
- /** The type */
- enum config_type type;
-
- /** The specific type for CONFIG_USER */
- const struct config_user_type *user_type;
-
- /** Description */
- const char *description;
-
- /** Optional value handler function, by type */
- union {
- err_t (*string) (void *ctx, char *value, struct error_info *err);
- err_t (*irc_chan) (void *ctx, struct irc_chan *chan, struct error_info *err);
- } func;
-
- /** Use handler function? */
- bool is_handler;
-
- /** Optional value? */
- bool optional;
-};
-
-/**
- * The maximum number of parameters that a single option can have, although this includes the terminating NULL
- */
-#define CONFIG_PARAMS_MAX (15 + 1)
-
-/**
- * The maximum number of values that an option can access, including the terminating NULL
- */
-#define CONFIG_VALUES_MAX CONFIG_PARAMS_MAX
-
-/**
- * A more complicated configuration option that can have multiple parameters
- */
-struct config_option {
- /** The name of the config option */
- const char *name;
-
- /** The list of parameters */
- const struct config_param params[CONFIG_PARAMS_MAX];
-
- /** The handler function */
- err_t (*func) (void *ctx, const struct config_option *option, const struct config_value values[], struct error_info *err);
-
- /** Help text */
- const char *help;
-};
-
-#define CONFIG_PARAM(name, type, desc, optional) \
- { name, type, NULL, desc, { NULL }, false, optional }
-
-#define CONFIG_PARAM_OPTIONAL(name, type, desc) \
- CONFIG_PARAM(name, type, desc, true)
-
-#define CONFIG_PARAM_USER(name, user_type, desc, optional) \
- { name, CONFIG_USER, user_type, desc, { NULL }, false, optional }
-
-#define CONFIG_PARAM_USER_OPTIONAL(name, user_type, desc) \
- CONFIG_PARAM_USER(name, user_type, desc, true)
-
-#define CONFIG_PARAM_HANDLER(name, type, desc, _func_name_, func) \
- { name, type, NULL, desc, {._func_name_ = func }, true, false }
-
-#define CONFIG_PARAM_END \
- { NULL, 0, NULL, NULL, { NULL }, false, false }
-
-#define CONFIG_OPT(name, func, help, ...) \
- { name, { __VA_ARGS__, CONFIG_PARAM_END }, func, help }
-
-#define CONFIG_OPT_STRING(name, func, desc, help) \
- CONFIG_OPT(name, NULL, help, CONFIG_PARAM_HANDLER(name, CONFIG_STRING, desc, string, func))
-
-#define CONFIG_OPT_IRC_CHAN(name, func, desc, help) \
- CONFIG_OPT(name, NULL, help, CONFIG_PARAM_HANDLER(name, CONFIG_IRC_CHAN, desc, irc_chan, func))
-
-#define CONFIG_OPT_END \
- { NULL, {}, NULL, NULL }
-
-#define CONFIG_OPTIONS(...) \
- { __VA_ARGS__, CONFIG_OPT_END }
-
-#define CONFIG_VALUE(type, _name_, value) \
- { (type), { ._name_ = value } }
-
-#define CONFIG_VALUE_STRING(str_value) \
- CONFIG_VALUE(CONFIG_STRING, string, (str_value))
-
-#define CONFIG_VALUE_IRC_CHAN(irc_chan_value) \
- CONFIG_VALUE(CONFIG_IRC_CHAN, irc_chan, (irc_chan_value))
-
-#define CONFIG_VALUE_USER(user_type, user_value) \
- CONFIG_VALUE(CONFIG_USER, user, { user_type, user_value })
-
-#define CONFIG_VALUE_END \
- { 0, { NULL } }
-
-/**
- * Lookup a config option by name.
- *
- * @param options a CONFIG_OPT_END-terminated array of config_option's
- * @param name the config_option::name to look up
- * @param err returned error info if not found
- * @return a direct pointer to the config_option if found
- */
-const struct config_option* config_lookup (const struct config_option *options, const char *name, struct error_info *err);
-
-/**
- * Returns the number of params that an option has
- *
- * @param option the option to count
- * @return the number of params
- */
-int config_params_count (const struct config_option *option);
-
-// XXX: move this into nexus
-#include "nexus.h"
-/**
- * Parse a raw value into a suitable configuration value for the given param, based on its type.
- *
- * Since this needs to access the application state, you need to pass in the nexus as an argument.
- *
- * Formats supported:
- * CONFIG_IRC_CHAN - uses a '<network>/<channel>' format and irc_client_get_chan
- *
- */
-err_t config_parse_param (const struct config_param *param, struct nexus *nexus, struct config_value *value, char *raw_value, struct error_info *err);
-
-/**
- * Parse a raw value into a suitable configuration value for a single-param option, based on the config option type.
- *
- * @param option the option to parse the value for, use config_lookup to find it
- * @param nexus the application state
- * @param value the returned value, if succesfull
- * @param raw_value the raw value to parse based on the type
- * @param err returned error info
- */
-err_t config_parse (const struct config_option *option, struct nexus *nexus, struct config_value *value, char *raw_value, struct error_info *err);
-
-/**
- * Apply a list of parsed configuration values to the given config_option struct.
- *
- * The config option handlers take a context argument; the value of this depends on the implementor of the config_option.
- *
- * @param option the option to apply
- * @param ctx the context pointer for the option handler
- * @param values the NULL-terminated array of parsed values
- * @param err returned error info
- */
-err_t config_apply_opt (const struct config_option *option, void *ctx, const struct config_value values[], struct error_info *err);
-
-/**
- * Apply a list of parsed configuration values for the named config opt.
- *
- * @param options a CONFIG_OPT_END-terminated array of config_option's
- * @param ctx the context pointer for the option handler
- * @param name the config_option::name to look up
- * @param values the NULL-termianted array of parsed values
- * @param err returned error info
- */
-err_t config_apply (const struct config_option *options, void *ctx, const char *name, const struct config_value values[], struct error_info *err);
-
-/**
- * Apply a string value for the named config opt.
- *
- * @param options a CONFIG_OPT_END-terminated array of config_option's
- * @param ctx the context pointer for the option handler
- * @param name the config_option::name to look up
- * @param value the string value
- * @param err returned error info
- */
-err_t config_apply_string (const struct config_option *options, void *ctx, const char *name, char *value, struct error_info *err);
-
-/**
- * Apply an irc_chan value for the named config opt.
- *
- * @param options a CONFIG_OPT_END-terminated array of config_option's
- * @param ctx the context pointer for the option handler
- * @param name the config_option::name to look up
- * @param value the irc_chan value
- * @param err returned error info
- */
-err_t config_apply_irc_chan (const struct config_option *options, void *ctx, const char *name, struct irc_chan *value, struct error_info *err);
-
-/**
- * Parse and apply a configuration value for the named config opt.
- *
- * See config_parse() for more info.
- *
- * @param options a CONFIG_OPT_END-terminated array of config_option's
- * @param nexus the application state
- * @param ctx the context pointer for the option handler
- * @param name the config_option::name to look up
- * @param raw_value the raw value to parse
- * @param err returned error info
- *
- * @see config_parse
- */
-err_t config_apply_raw (const struct config_option option[], struct nexus *nexus, void *ctx, const char *name, char *raw_value, struct error_info *err);
-
-/**
- * Lookup a config_value by name from the given list of config_values for the given config_option
- */
-const struct config_value* config_get_value (const struct config_option *option, const struct config_value values[], const char *name);
-
-const char* config_get_string (const struct config_option *option, const struct config_value values[], const char *name);
-struct irc_chan* config_get_irc_chan (const struct config_option *option, const struct config_value values[], const char *name);
-void* config_get_user (const struct config_option *option, const struct config_value values[], const char *name, const struct config_user_type *user_type);
-
-#endif
--- a/src/console.c Wed May 27 23:07:00 2009 +0300
+++ b/src/console.c Wed May 27 23:57:48 2009 +0300
@@ -138,7 +138,7 @@
}
err_t console_init (struct console **console_ptr, struct event_base *ev_base, const struct console_config *config,
- const struct console_callbacks *callbacks, void *cb_arg, struct error_info *err)
+ const struct console_callbacks *callbacks, void *cb_arg, error_t *err)
{
struct console *console = &_console;
--- a/src/console.h Wed May 27 23:07:00 2009 +0300
+++ b/src/console.h Wed May 27 23:57:48 2009 +0300
@@ -81,7 +81,7 @@
* @param err returned error info
*/
err_t console_init (struct console **console_ptr, struct event_base *ev_base, const struct console_config *config,
- const struct console_callbacks *callbacks, void *cb_arg, struct error_info *err);
+ const struct console_callbacks *callbacks, void *cb_arg, error_t *err);
/**
* Replace the current callbacks with the given new ones.
--- a/src/irc_chan.c Wed May 27 23:07:00 2009 +0300
+++ b/src/irc_chan.c Wed May 27 23:57:48 2009 +0300
@@ -275,7 +275,7 @@
{ NULL, NULL }
};
-err_t irc_chan_create (struct irc_chan **chan_ptr, struct irc_net *net, const struct irc_chan_info *info, struct error_info *err)
+err_t irc_chan_create (struct irc_chan **chan_ptr, struct irc_net *net, const struct irc_chan_info *info, error_t *err)
{
struct irc_chan *chan;
--- a/src/irc_chan.h Wed May 27 23:07:00 2009 +0300
+++ b/src/irc_chan.h Wed May 27 23:57:48 2009 +0300
@@ -156,7 +156,7 @@
* @param info the channel's identifying information
* @param err error codes are returned via this
*/
-err_t irc_chan_create (struct irc_chan **chan_ptr, struct irc_net *net, const struct irc_chan_info *info, struct error_info *err);
+err_t irc_chan_create (struct irc_chan **chan_ptr, struct irc_net *net, const struct irc_chan_info *info, error_t *err);
/**
* Destroy irc_chan state, without doing anything to the network
--- a/src/irc_client.c Wed May 27 23:07:00 2009 +0300
+++ b/src/irc_client.c Wed May 27 23:57:48 2009 +0300
@@ -4,7 +4,7 @@
#include <stdlib.h>
#include <string.h>
-err_t irc_client_create (struct irc_client **client_ptr, struct error_info *err)
+err_t irc_client_create (struct irc_client **client_ptr, error_t *err)
{
struct irc_client *client;
@@ -50,7 +50,7 @@
)) == NULL \
)
-err_t irc_client_add_net (struct irc_client *client, struct irc_net **net_ptr, const struct irc_net_info *_net_info, struct error_info *err)
+err_t irc_client_add_net (struct irc_client *client, struct irc_net **net_ptr, const struct irc_net_info *_net_info, error_t *err)
{
struct irc_net *net;
struct irc_net_info net_info = {
--- a/src/irc_client.h Wed May 27 23:07:00 2009 +0300
+++ b/src/irc_client.h Wed May 27 23:57:48 2009 +0300
@@ -4,7 +4,9 @@
/**
* @file
*
- * Defines the high-level, full-featured IRC 'client' state, which essentially manipulates a set of irc_net's
+ * Defines the high-level, full-featured IRC 'client' state, which essentially manipulates a set of irc_net's.
+ *
+ * XXX: rename to something else
*/
#include "irc_net.h"
#include <sys/queue.h>
@@ -43,7 +45,7 @@
/**
* Construct a new irc_client
*/
-err_t irc_client_create (struct irc_client **client_ptr, struct error_info *err);
+err_t irc_client_create (struct irc_client **client_ptr, error_t *err);
/**
* Destroy the irc_client, also destroying all networks
@@ -71,7 +73,7 @@
* @param net_info info required to identify and connect to the network
* @param err returned error info
*/
-err_t irc_client_add_net (struct irc_client *client, struct irc_net **net_ptr, const struct irc_net_info *net_info, struct error_info *err);
+err_t irc_client_add_net (struct irc_client *client, struct irc_net **net_ptr, const struct irc_net_info *net_info, error_t *err);
/**
* Get a pre-existing IRC network by name
--- a/src/irc_conn.c Wed May 27 23:07:00 2009 +0300
+++ b/src/irc_conn.c Wed May 27 23:57:48 2009 +0300
@@ -13,7 +13,7 @@
*
* For conveniance, this returns the ERROR_CODE
*/
-static err_t irc_conn_set_error (struct irc_conn *conn, struct error_info *err)
+static err_t irc_conn_set_error (struct irc_conn *conn, error_t *err)
{
// notify user callback
conn->callbacks.on_error(conn, err, conn->cb_arg);
@@ -27,7 +27,7 @@
*/
static err_t irc_conn_set_nickname (struct irc_conn *conn, const char *nickname)
{
- struct error_info err;
+ error_t err;
// drop old nickname
free(conn->nickname);
@@ -213,7 +213,7 @@
/**
* Transport failed
*/
-static void irc_conn_on_error (struct error_info *err, void *arg)
+static void irc_conn_on_error (error_t *err, void *arg)
{
struct irc_conn *conn = arg;
--- a/src/irc_conn.h Wed May 27 23:07:00 2009 +0300
+++ b/src/irc_conn.h Wed May 27 23:57:48 2009 +0300
@@ -7,6 +7,8 @@
* Support for connections to IRC servers, a rather fundamental thing. This holds the line_proto to handle the network
* communications, and then takes care of sending/receiving commands, as well as some core functionality like
* responding to PING requests, and tracking our current nickname.
+ *
+ * XXX: split into irc_proto and irc_client
*/
struct irc_conn;
@@ -48,7 +50,7 @@
*
* NOTE: Implementing this callback is mandatory
*/
- void (*on_error) (struct irc_conn *conn, struct error_info *err, void *arg);
+ void (*on_error) (struct irc_conn *conn, error_t *err, void *arg);
/**
* The connection was closed cleanly after irc_conn_QUIT.
--- a/src/irc_net.c Wed May 27 23:07:00 2009 +0300
+++ b/src/irc_net.c Wed May 27 23:57:48 2009 +0300
@@ -39,7 +39,7 @@
{
struct irc_net *net = arg;
struct irc_chan *chan = NULL;
- struct error_info err;
+ error_t err;
(void) conn;
@@ -55,7 +55,7 @@
/**
* Our irc_conn has spontaneously failed, destroy it
*/
-static void irc_net_conn_error (struct irc_conn *conn, struct error_info *err, void *arg)
+static void irc_net_conn_error (struct irc_conn *conn, error_t *err, void *arg)
{
struct irc_net *net = arg;
@@ -250,7 +250,7 @@
{ NULL, NULL }
};
-err_t irc_net_create (struct irc_net **net_ptr, const struct irc_net_info *info, struct error_info *err)
+err_t irc_net_create (struct irc_net **net_ptr, const struct irc_net_info *info, error_t *err)
{
struct irc_net *net;
@@ -319,7 +319,7 @@
free(net);
}
-err_t irc_net_add_chan (struct irc_net *net, struct irc_chan **chan_ptr, const struct irc_chan_info *info, struct error_info *err)
+err_t irc_net_add_chan (struct irc_net *net, struct irc_chan **chan_ptr, const struct irc_chan_info *info, error_t *err)
{
struct irc_chan *chan;
--- a/src/irc_net.h Wed May 27 23:07:00 2009 +0300
+++ b/src/irc_net.h Wed May 27 23:57:48 2009 +0300
@@ -95,7 +95,7 @@
* @param info network information, used to connect and register
* @param err return error info
*/
-err_t irc_net_create (struct irc_net **net, const struct irc_net_info *info, struct error_info *err);
+err_t irc_net_create (struct irc_net **net, const struct irc_net_info *info, error_t *err);
/**
* Destroy an irc_net state without closing anything cleanly. This destroys the irc_conn, if any, and any irc_chans as
@@ -113,7 +113,7 @@
* @param info the info required to identify and join the channel
* @param err return error info
*/
-err_t irc_net_add_chan (struct irc_net *net, struct irc_chan **chan_ptr, const struct irc_chan_info *info, struct error_info *err);
+err_t irc_net_add_chan (struct irc_net *net, struct irc_chan **chan_ptr, const struct irc_chan_info *info, error_t *err);
/**
* Look up an existing irc_chan by name, returning NULL if not found.
--- a/src/irc_queue.c Wed May 27 23:07:00 2009 +0300
+++ b/src/irc_queue.c Wed May 27 23:57:48 2009 +0300
@@ -125,7 +125,7 @@
}
}
-err_t irc_queue_create (struct irc_queue **queue_ptr, struct event_base *ev_base, struct line_proto *lp, struct error_info *err)
+err_t irc_queue_create (struct irc_queue **queue_ptr, struct event_base *ev_base, struct line_proto *lp, error_t *err)
{
struct irc_queue *queue;
--- a/src/irc_queue.h Wed May 27 23:07:00 2009 +0300
+++ b/src/irc_queue.h Wed May 27 23:57:48 2009 +0300
@@ -69,7 +69,7 @@
/**
* Create a new irc_queue for use with the given line_proto
*/
-err_t irc_queue_create (struct irc_queue **queue_ptr, struct event_base *ev_base, struct line_proto *lp, struct error_info *err);
+err_t irc_queue_create (struct irc_queue **queue_ptr, struct event_base *ev_base, struct line_proto *lp, error_t *err);
/**
* Process a line, either sending it directly, or enqueueing it, based on the timer state.
--- a/src/lib/error.c Wed May 27 23:07:00 2009 +0300
+++ b/src/lib/error.c Wed May 27 23:57:48 2009 +0300
@@ -1,6 +1,14 @@
#include "error.h"
#include "log.h"
+const struct error_list general_errors = ERROR_LIST("general",
+ ERROR_TYPE( ERR_MEM, "memory allocation error" ),
+ ERROR_TYPE_STRING( ERR_NOT_IMPLEMENTED, "function not implmented" ),
+ ERROR_TYPE_STRING( ERR_MISC, "miscellaneous error" ),
+ ERROR_TYPE_STRING( ERR_CMD_OPT, "invalid command line option" ),
+ ERROR_TYPE( ERR_UNKNOWN, "unknown error" )
+);
+
const struct error_type* error_lookup_code (const struct error_list *list, err_t code)
{
const struct error_type *type;
@@ -43,12 +51,12 @@
return type;
}
-const char* error_name (const error_t *err)
+const char* error_name (const struct error_list *list, err_t code);
{
struct error_type *type;
// just do a lookup
- if ((type = error_lookup(err)))
+ if ((type = error_lookup_code(list, code)))
return "[unknown]";
else
@@ -119,6 +127,11 @@
return true;
}
+void error_reset (error_t *err)
+{
+ memset(err, 0, sizeof(*err));
+}
+
/**
* Advance error stack to next position, and return pointer.
*
--- a/src/lib/error.h Wed May 27 23:07:00 2009 +0300
+++ b/src/lib/error.h Wed May 27 23:57:48 2009 +0300
@@ -11,6 +11,7 @@
* tree has it's own set of error codes, and then error info contexts contain a stack of these error codes, which can
* then be used to trace the error down.
*/
+#include <stdbool.h>
/**
* The type used to represent a scalar error code, there are only unique per level.
@@ -133,7 +134,7 @@
} stack[ERROR_DEPTH_MAX];
/** Current top of stack for accumulating errors, NULL means empty stack */
- struct error_info_level *cur;
+ struct error_item *cur;
/** Type info for external error info */
const struct error_extra_type *extra_type;
@@ -154,9 +155,9 @@
const struct error_type* error_lookup (const error_t *err);
/**
- * Return the error name for the lowest level error in the given state's stack.
+ * Return the error name for the given code.
*/
-const char* error_name (const error_t *err);
+const char* error_name (const struct error_list *list, err_t code);
/**
* Maximum length of messages returned by error_msg
@@ -175,17 +176,16 @@
/**
- * Reset the given error state to an empty state
+ * Reset the given error state to an empty state.
+ *
+ * This is just the same as memset() with zero.
*/
-static inline void error_reset (error_t *err)
-{
- memset(err, 0, sizeof(*err));
-}
+void error_reset (error_t *err);
/**
* Evaluates to the current top of the error stack,
*/
-static struct error_item* error_top (const error_t *err)
+static struct error_item* error_top (error_t *err)
{
return err->cur ? err->cur : err->stack;
}
@@ -275,4 +275,18 @@
*/
#define NOT_REACHED(val) error_abort("%s = %#x", #val, (int) (val));
+/**
+ * General-purpose errors that may be useful and don't belong in any more specific namespace.
+ */
+enum general_error_code {
+ ERR_GENERAL_NONE,
+ ERR_MEM, ///< memory allocation error
+ ERR_NOT_IMPLEMENTED, ///< function not implmented: <func>
+ ERR_MISC, ///< miscellaneous error: <error>
+ ERR_CMD_OPT, ///< invalid command line option: <error> - XXX: replace with something getopt
+ ERR_UNKNOWN, ///< unknown error
+};
+
+const struct error_list general_errors;
+
#endif /* LIBQMSK_ERROR_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/log.c Wed May 27 23:57:48 2009 +0300
@@ -0,0 +1,165 @@
+
+#include "log.h"
+#include "str.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+
+/**
+ * The default log output func
+ */
+void log_default_func (const char *line, void *arg)
+{
+ (void) arg;
+
+ // display
+ printf("%s\n", line);
+}
+
+/**
+ * The global log level
+ */
+static enum log_level _log_level = LOG_LEVEL_DEFAULT;
+
+/**
+ * The global log output func
+ */
+static struct log_output_ctx {
+ /** The function itself */
+ log_output_func func;
+
+ /** The arg */
+ void *arg;
+} _log_output_ctx = { log_default_func, NULL };
+
+/**
+ * List of log level names
+ */
+const char *log_level_names[] = {
+ "DEBUG",
+ "INFO",
+ "WARN",
+ "ERROR",
+ "FATAL",
+ NULL
+};
+
+#define _LOG_LEVEL_NAME(ll) case LOG_ ## ll: return #ll;
+const char *log_level_name (enum log_level level)
+{
+ switch (level) {
+ _LOG_LEVEL_NAME(DEBUG)
+ _LOG_LEVEL_NAME(INFO)
+ _LOG_LEVEL_NAME(WARN)
+ _LOG_LEVEL_NAME(ERROR)
+ _LOG_LEVEL_NAME(FATAL)
+ default: return "???";
+ }
+}
+#undef _LOG_LEVEL_NAME
+
+void set_log_level (enum log_level level)
+{
+ // meep meep
+ _log_level = level;
+}
+
+void log_set_func (log_output_func func, void *arg)
+{
+ // replace the current one
+ _log_output_ctx.func = func ? func : log_default_func;
+ _log_output_ctx.arg = arg;
+}
+
+void log_output_tag_va (enum log_level level, const char *tag, const char *func, const char *user_fmt, va_list user_fmtargs, const char *log_fmt, va_list log_fmtargs)
+{
+ char buf[LOG_MSG_MAX], *buf_ptr = buf;
+ size_t buf_size = sizeof(buf);
+
+ // filter out?
+ if (level < _log_level)
+ return;
+
+ // output the header
+ buf_ptr += str_advance(NULL, &buf_size, str_append_fmt(buf_ptr, buf_size, "[%5s] %20s : ", tag, func));
+
+ // output the user data
+ if (user_fmt)
+ buf_ptr += str_advance(NULL, &buf_size, str_append_fmt_va(buf_ptr, buf_size, user_fmt, user_fmtargs));
+
+ // output the suffix
+ if (log_fmt)
+ buf_ptr += str_advance(NULL, &buf_size, str_append_fmt_va(buf_ptr, buf_size, log_fmt, log_fmtargs));
+
+ // send it to the output func
+ _log_output_ctx.func(buf, _log_output_ctx.arg);
+
+}
+
+void log_output_tag (enum log_level level, const char *tag, const char *func, const char *user_fmt, va_list user_fmtargs, const char *log_fmt, ...)
+{
+ va_list vargs;
+
+ va_start(vargs, log_fmt);
+ log_output_tag_va(level, tag, func, user_fmt, user_fmtargs, log_fmt, vargs);
+ va_end(vargs);
+}
+
+void _log_msg (enum log_level level, const char *func, const char *format, ...)
+{
+ va_list vargs;
+
+ // formatted output: no suffix
+ va_start(vargs, format);
+ log_output_tag(level, log_level_name(level), func, format, vargs, NULL);
+ va_end(vargs);
+}
+
+void _log_msg_va2 (enum log_level level, const char *func, const char *fmt1, va_list fmtargs1, const char *fmt2, va_list fmtargs2)
+{
+ log_output_tag_va(level, log_level_name(level), func, fmt1, fmtargs1, fmt2, fmtargs2);
+}
+
+void _log_err (enum log_level level, const struct error_list *list, err_t code, const char *func, const char *format, ...)
+{
+ va_list vargs;
+
+ // formatted output: suffix error_name()
+ va_start(vargs, format);
+ log_output_tag(level, log_level_name(level), func, format, vargs, ": %s", error_name(list, code));
+ va_end(vargs);
+}
+
+void _log_error (enum log_level level, const error_t *err, const char *func, const char *format, ...)
+{
+ va_list vargs;
+
+ // formatted output: suffix error_msg()
+ va_start(vargs, format);
+ log_output_tag(level, log_level_name(level), func, format, vargs, ": %s", error_msg(err));
+ va_end(vargs);
+}
+
+void _log_perr (enum log_level level, const char *func, const char *format, ...)
+{
+ va_list vargs;
+
+ // formatted output: suffix strerror()
+ va_start(vargs, format);
+ log_output_tag(level, log_level_name(level), func, format, vargs, ": %s", strerror(errno));
+ va_end(vargs);
+}
+
+void _log_exit (enum log_level level, int exit_code, const char *func, const char *format, ...)
+{
+ va_list vargs;
+
+ // formatted output without any suffix
+ va_start(vargs, format);
+ log_output_tag(level, "EXIT", func, format, vargs, NULL);
+ va_end(vargs);
+
+ // exit
+ exit(exit_code);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/log.h Wed May 27 23:57:48 2009 +0300
@@ -0,0 +1,153 @@
+#ifndef LOG_H
+#define LOG_H
+
+/** @file log.h
+ *
+ * Local logging functions
+ */
+#include "error.h"
+#include <stdarg.h>
+
+/**
+ * Log level definitions
+ *
+ * XXX: these names conflict with <syslog.h>
+ */
+enum log_level {
+ LOG_DEBUG,
+ LOG_INFO,
+ LOG_WARN,
+ LOG_ERROR,
+ LOG_FATAL,
+};
+
+/**
+ * List of log_level values as strings, NULL-terminated
+ */
+extern const char *log_level_names[];
+
+/**
+ * The default log level
+ */
+#define LOG_LEVEL_DEFAULT LOG_INFO
+
+/**
+ * Maximum length of a log message, including terminating NUL
+ */
+#define LOG_MSG_MAX 1024
+
+/**
+ * Log output function, for displaying a line of text, without any trailing newline.
+ */
+typedef void (*log_output_func) (const char *line, void *arg);
+
+/**
+ * Set the current log level to filter out messages below the given level
+ */
+void set_log_level (enum log_level level);
+
+/**
+ * Set the current log output function, replacing any default or previous value.
+ *
+ * Pass in func as NULL to revert to the default handler.
+ */
+void log_set_func (log_output_func func, void *arg);
+
+/**
+ * Internal logging func, meant for using custom level tags. This performs the filtering of log levels.
+ *
+ * Format the full output line, and pass it to the log_output_func. The line will be of the form:
+ * "[<tag>] <func> : <user_fmt> [ <log_fmt> ]"
+ */
+void log_output_tag (enum log_level level, const char *tag, const char *func, const char *user_fmt, va_list user_fmtargs, const char *log_fmt, ...)
+ __attribute__ ((format (printf, 6, 7)));
+
+/**
+ * va_list version of log_output_tag
+ */
+void log_output_tag_va (enum log_level level, const char *tag, const char *func, const char *user_fmt, va_list user_fmtargs, const char *log_fmt, va_list log_fmtargs);
+
+/**
+ * Log a message with the given level
+ */
+#define log_msg(level, ...) _log_msg(level, __func__, __VA_ARGS__)
+void _log_msg (enum log_level level, const char *func, const char *format, ...)
+ __attribute__ ((format (printf, 3, 4)));
+
+/**
+ * Log a message with the given level with the given format and varargs
+ */
+#define log_msg_va2(level, fmt1, vargs1, fmt2, vargs2) _log_msg_va(level, __func__, fmt1, vargs1, fmt2, vargs2)
+void _log_msg_va2 (enum log_level level, const char *func, const char *fmt1, va_list fmtargs1, const char *fmt2, va_list fmtargs2);
+
+
+/**
+ * Shorthand for log_msg
+ */
+#define log_debug(...) log_msg(LOG_DEBUG, __VA_ARGS__)
+#define log_info(...) log_msg(LOG_INFO, __VA_ARGS__)
+#define log_warn(...) log_msg(LOG_WARN, __VA_ARGS__)
+/* #define log_error(...) log_msg(LOG_ERROR, __func__, __VA_ARGS__) */
+#define log_fatal(...) log_msg(LOG_FATAL, __VA_ARGS__)
+
+/**
+ * Log a message with the given level, appending the formatted error code name
+ */
+#define log_err(err_list, err_code, ...) _log_err(LOG_ERROR, err_list, err_code, __func__, __VA_ARGS__)
+#define log_warn_err(err_list, err_code, ...) _log_err(LOG_WARN, err_list, err_code, __func__, __VA_ARGS__)
+void _log_err (enum log_level level, const struct error_list *list, err_t code, const char *func, const char *format, ...)
+ __attribute__ ((format (printf, 5, 6)));
+
+/**
+ * Log a message with the given level, appending the formatted error message
+ */
+#define log_error(err_info, ...) _log_error(LOG_ERROR, err_info, __func__, __VA_ARGS__)
+#define log_warn_error(err_info, ...) _log_error(LOG_WARN, err_info, __func__, __VA_ARGS__)
+void _log_error (enum log_level level, const error_t *err, const char *func, const char *format, ...)
+ __attribute__ ((format (printf, 4, 5)));
+
+/**
+ * Log using errno.
+ */
+#define log_perr(...) _log_perr(LOG_ERROR, __func__, __VA_ARGS__)
+void _log_perr (enum log_level level, const char *func, const char *format, ...)
+ __attribute__ ((format (printf, 3, 4)));
+
+/**
+ * Log with an [EXIT] tag at given level, and then exit with given exit code
+ */
+#define log_exit(level, exit_code, ...) _log_exit(level, exit_code, __func__, __VA_ARGS__)
+void _log_exit (enum log_level level, int exit_code, const char *func, const char *format, ...)
+ __attribute__ ((format (printf, 4, 5)))
+ __attribute__ ((noreturn));
+
+
+/**
+ * log_fatal + exit failure
+ */
+#define FATAL(...) do { log_fatal(__VA_ARGS__); abort(); } while (0)
+
+/**
+ * log_err + exit failure
+ */
+#define FATAL_ERR(err_code, ...) do { _log_err(LOG_FATAL, err_code, __func__, __VA_ARGS__); abort(); } while (0)
+
+/**
+ * log_err_info + exit failure
+ */
+#define FATAL_ERROR(err_info, ...) do { _log_error(LOG_FATAL, err_info, __func__, __VA_ARGS__); abort(); } while (0)
+
+/**
+ * log_perr + exit failure
+ */
+#define FATAL_PERROR(...) do { _log_perr(LOG_FATAL, __func__, __VA_ARGS__); abort(); } while (0)
+
+/**
+ * Exit with given code, also logging a message at LOG_INFO with an [EXIT] tag
+ */
+#define EXIT_INFO(exit_code, ...) log_exit(LOG_INFO, exit_code, __VA_ARGS__)
+#define EXIT_WARN(exit_code, ...) log_exit(LOG_WARN, exit_code, __VA_ARGS__)
+#define EXIT_ERROR(exit_code, ...) log_exit(LOG_ERROR, exit_code, __VA_ARGS__)
+#define EXIT_FATAL(exit_code, ...) log_exit(LOG_FATAL, exit_code, __VA_ARGS__)
+
+#endif /* LOG_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/object.c Wed May 27 23:57:48 2009 +0300
@@ -0,0 +1,53 @@
+#include "object.h"
+
+#include <stdlib.h>
+#include <assert.h>
+
+void object_init (struct object *obj, const struct object_type *type)
+{
+ // trip on bugs
+ assert(!obj->type);
+
+ // set type
+ obj->type = type;
+}
+
+bool object_type_check (const struct object_type *obj_type, const struct object_type *type)
+{
+ const struct object_type *t;
+
+ // sanity-check
+ assert(obj_type && type);
+
+ // look for a matching type in the type's inheritance tree
+ for (t = obj_type; t; t = t->parent)
+ if (t == type)
+ break;
+
+ // t will be (parent == NULL) if we didn't find any matching type
+ return (t != NULL);
+}
+
+bool object_check (struct object *obj, const struct object_type *type)
+{
+ // sanity check
+ assert(obj && type);
+
+ return object_type_check(obj->type, type);
+}
+
+void* object_cast (struct object *obj, const struct object_type *type)
+{
+ assert(object_type_check(obj->type, type));
+
+ // ok, return as void*
+ return obj;
+}
+
+const void* object_type (struct object *obj, const struct object_type *type)
+{
+ assert(object_type_check(obj->type, type));
+
+ // ok, return as void*
+ return obj->type;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/object.h Wed May 27 23:57:48 2009 +0300
@@ -0,0 +1,56 @@
+#ifndef OBJECT_H
+#define OBJECT_H
+
+/**
+ * @file
+ *
+ * The thing feared by every C-programmer - calling C structs "objects".
+ */
+#include <stdbool.h>
+
+/**
+ * Basic object type info
+ */
+struct object_type {
+ /** Parent type for nested structs, NULL if the parent is object_type */
+ const struct object_type *parent;
+};
+
+/**
+ * Basic object state
+ */
+struct object {
+ /** Object's type */
+ const struct object_type *type;
+};
+
+/**
+ * Initialize an object to the given type.
+ */
+void object_init (struct object *obj, const struct object_type *type);
+
+/**
+ * Check that the given object type is compatible with the given type.
+ */
+bool object_type_check (const struct object_type *obj_type, const struct object_type *type);
+
+/**
+ * Check that the given object "implements" the given type.
+ */
+bool object_check (struct object *obj, const struct object_type *type);
+
+/**
+ * Return the given object as a pointer suitable for casting to the struct for the given type.
+ *
+ * It is a bug to call this with an object that's not compatible with the given type.
+ */
+void* object_cast (struct object *obj, const struct object_type *type);
+
+/**
+ * Return a pointer to the object's type that is compatible with the given type.
+ *
+ * It is a bug to call this with a object that's not compatible with the given type.
+ */
+const void* object_type (struct object *obj, const struct object_type *type);
+
+#endif /* OBJECT_H */
--- a/src/line_proto.h Wed May 27 23:07:00 2009 +0300
+++ b/src/line_proto.h Wed May 27 23:57:48 2009 +0300
@@ -22,7 +22,7 @@
void (*on_line) (char *line, void *arg);
/** Transport failed, the line_proto is corrupt, you should call line_proto_release next. */
- void (*on_error) (struct error_info *err, void *arg);
+ void (*on_error) (const error_t *err, void *arg);
};
/**
@@ -72,7 +72,7 @@
/**
* Get current error_info*
*/
-const struct error_info* line_proto_error (struct line_proto *lp);
+const error_t* line_proto_error (struct line_proto *lp);
/**
* Destroy any buffers and the underlying transport.
--- a/src/log.c Wed May 27 23:07:00 2009 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,165 +0,0 @@
-
-#include "log.h"
-#include "str.h"
-
-#include <stdio.h>
-#include <stdarg.h>
-#include <string.h>
-
-/**
- * The default log output func
- */
-void log_default_func (const char *line, void *arg)
-{
- (void) arg;
-
- // display
- printf("%s\n", line);
-}
-
-/**
- * The global log level
- */
-static enum log_level _log_level = LOG_LEVEL_DEFAULT;
-
-/**
- * The global log output func
- */
-static struct log_output_ctx {
- /** The function itself */
- log_output_func func;
-
- /** The arg */
- void *arg;
-} _log_output_ctx = { log_default_func, NULL };
-
-/**
- * List of log level names
- */
-const char *log_level_names[] = {
- "DEBUG",
- "INFO",
- "WARN",
- "ERROR",
- "FATAL",
- NULL
-};
-
-#define _LOG_LEVEL_NAME(ll) case LOG_ ## ll: return #ll;
-const char *log_level_name (enum log_level level)
-{
- switch (level) {
- _LOG_LEVEL_NAME(DEBUG)
- _LOG_LEVEL_NAME(INFO)
- _LOG_LEVEL_NAME(WARN)
- _LOG_LEVEL_NAME(ERROR)
- _LOG_LEVEL_NAME(FATAL)
- default: return "???";
- }
-}
-#undef _LOG_LEVEL_NAME
-
-void set_log_level (enum log_level level)
-{
- // meep meep
- _log_level = level;
-}
-
-void log_set_func (log_output_func func, void *arg)
-{
- // replace the current one
- _log_output_ctx.func = func ? func : log_default_func;
- _log_output_ctx.arg = arg;
-}
-
-void log_output_tag_va (enum log_level level, const char *tag, const char *func, const char *user_fmt, va_list user_fmtargs, const char *log_fmt, va_list log_fmtargs)
-{
- char buf[LOG_MSG_MAX], *buf_ptr = buf;
- size_t buf_size = sizeof(buf);
-
- // filter out?
- if (level < _log_level)
- return;
-
- // output the header
- buf_ptr += str_advance(NULL, &buf_size, str_append_fmt(buf_ptr, buf_size, "[%5s] %20s : ", tag, func));
-
- // output the user data
- if (user_fmt)
- buf_ptr += str_advance(NULL, &buf_size, str_append_fmt_va(buf_ptr, buf_size, user_fmt, user_fmtargs));
-
- // output the suffix
- if (log_fmt)
- buf_ptr += str_advance(NULL, &buf_size, str_append_fmt_va(buf_ptr, buf_size, log_fmt, log_fmtargs));
-
- // send it to the output func
- _log_output_ctx.func(buf, _log_output_ctx.arg);
-
-}
-
-void log_output_tag (enum log_level level, const char *tag, const char *func, const char *user_fmt, va_list user_fmtargs, const char *log_fmt, ...)
-{
- va_list vargs;
-
- va_start(vargs, log_fmt);
- log_output_tag_va(level, tag, func, user_fmt, user_fmtargs, log_fmt, vargs);
- va_end(vargs);
-}
-
-void _log_msg (enum log_level level, const char *func, const char *format, ...)
-{
- va_list vargs;
-
- // formatted output: no suffix
- va_start(vargs, format);
- log_output_tag(level, log_level_name(level), func, format, vargs, NULL);
- va_end(vargs);
-}
-
-void _log_msg_va2 (enum log_level level, const char *func, const char *fmt1, va_list fmtargs1, const char *fmt2, va_list fmtargs2)
-{
- log_output_tag_va(level, log_level_name(level), func, fmt1, fmtargs1, fmt2, fmtargs2);
-}
-
-void _log_err (enum log_level level, err_t err, const char *func, const char *format, ...)
-{
- va_list vargs;
-
- // formatted output: suffix error_name()
- va_start(vargs, format);
- log_output_tag(level, log_level_name(level), func, format, vargs, ": %s", error_name(err));
- va_end(vargs);
-}
-
-void _log_error (enum log_level level, const error_t *err, const char *func, const char *format, ...)
-{
- va_list vargs;
-
- // formatted output: suffix error_msg()
- va_start(vargs, format);
- log_output_tag(level, log_level_name(level), func, format, vargs, ": %s", error_msg(err));
- va_end(vargs);
-}
-
-void _log_perr (enum log_level level, const char *func, const char *format, ...)
-{
- va_list vargs;
-
- // formatted output: suffix strerror()
- va_start(vargs, format);
- log_output_tag(level, log_level_name(level), func, format, vargs, ": %s", strerror(errno));
- va_end(vargs);
-}
-
-void _log_exit (enum log_level level, int exit_code, const char *func, const char *format, ...)
-{
- va_list vargs;
-
- // formatted output without any suffix
- va_start(vargs, format);
- log_output_tag(level, "EXIT", func, format, vargs, NULL);
- va_end(vargs);
-
- // exit
- exit(exit_code);
-}
--- a/src/log.h Wed May 27 23:07:00 2009 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,153 +0,0 @@
-#ifndef LOG_H
-#define LOG_H
-
-/** @file log.h
- *
- * Local logging functions
- */
-#include "error.h"
-#include <stdarg.h>
-
-/**
- * Log level definitions
- *
- * XXX: these names conflict with <syslog.h>
- */
-enum log_level {
- LOG_DEBUG,
- LOG_INFO,
- LOG_WARN,
- LOG_ERROR,
- LOG_FATAL,
-};
-
-/**
- * List of log_level values as strings, NULL-terminated
- */
-extern const char *log_level_names[];
-
-/**
- * The default log level
- */
-#define LOG_LEVEL_DEFAULT LOG_INFO
-
-/**
- * Maximum length of a log message, including terminating NUL
- */
-#define LOG_MSG_MAX 1024
-
-/**
- * Log output function, for displaying a line of text, without any trailing newline.
- */
-typedef void (*log_output_func) (const char *line, void *arg);
-
-/**
- * Set the current log level to filter out messages below the given level
- */
-void set_log_level (enum log_level level);
-
-/**
- * Set the current log output function, replacing any default or previous value.
- *
- * Pass in func as NULL to revert to the default handler.
- */
-void log_set_func (log_output_func func, void *arg);
-
-/**
- * Internal logging func, meant for using custom level tags. This performs the filtering of log levels.
- *
- * Format the full output line, and pass it to the log_output_func. The line will be of the form:
- * "[<tag>] <func> : <user_fmt> [ <log_fmt> ]"
- */
-void log_output_tag (enum log_level level, const char *tag, const char *func, const char *user_fmt, va_list user_fmtargs, const char *log_fmt, ...)
- __attribute__ ((format (printf, 6, 7)));
-
-/**
- * va_list version of log_output_tag
- */
-void log_output_tag_va (enum log_level level, const char *tag, const char *func, const char *user_fmt, va_list user_fmtargs, const char *log_fmt, va_list log_fmtargs);
-
-/**
- * Log a message with the given level
- */
-#define log_msg(level, ...) _log_msg(level, __func__, __VA_ARGS__)
-void _log_msg (enum log_level level, const char *func, const char *format, ...)
- __attribute__ ((format (printf, 3, 4)));
-
-/**
- * Log a message with the given level with the given format and varargs
- */
-#define log_msg_va2(level, fmt1, vargs1, fmt2, vargs2) _log_msg_va(level, __func__, fmt1, vargs1, fmt2, vargs2)
-void _log_msg_va2 (enum log_level level, const char *func, const char *fmt1, va_list fmtargs1, const char *fmt2, va_list fmtargs2);
-
-
-/**
- * Shorthand for log_msg
- */
-#define log_debug(...) log_msg(LOG_DEBUG, __VA_ARGS__)
-#define log_info(...) log_msg(LOG_INFO, __VA_ARGS__)
-#define log_warn(...) log_msg(LOG_WARN, __VA_ARGS__)
-/* #define log_error(...) log_msg(LOG_ERROR, __func__, __VA_ARGS__) */
-#define log_fatal(...) log_msg(LOG_FATAL, __VA_ARGS__)
-
-/**
- * Log a message with the given level, appending the formatted error code name
- */
-#define log_err(err_code, ...) _log_err(LOG_ERROR, err_code, __func__, __VA_ARGS__)
-#define log_warn_err(err_code, ...) _log_err(LOG_WARN, err_code, __func__, __VA_ARGS__)
-void _log_err (enum log_level level, err_t err, const char *func, const char *format, ...)
- __attribute__ ((format (printf, 4, 5)));
-
-/**
- * Log a message with the given level, appending the formatted error message
- */
-#define log_error(err_info, ...) _log_error(LOG_ERROR, err_info, __func__, __VA_ARGS__)
-#define log_warn_error(err_info, ...) _log_error(LOG_WARN, err_info, __func__, __VA_ARGS__)
-void _log_error (enum log_level level, const error_t *err, const char *func, const char *format, ...)
- __attribute__ ((format (printf, 4, 5)));
-
-/**
- * Log using errno.
- */
-#define log_perr(...) _log_perr(LOG_ERROR, __func__, __VA_ARGS__)
-void _log_perr (enum log_level level, const char *func, const char *format, ...)
- __attribute__ ((format (printf, 3, 4)));
-
-/**
- * Log with an [EXIT] tag at given level, and then exit with given exit code
- */
-#define log_exit(level, exit_code, ...) _log_exit(level, exit_code, __func__, __VA_ARGS__)
-void _log_exit (enum log_level level, int exit_code, const char *func, const char *format, ...)
- __attribute__ ((format (printf, 4, 5)))
- __attribute__ ((noreturn));
-
-
-/**
- * log_fatal + exit failure
- */
-#define FATAL(...) do { log_fatal(__VA_ARGS__); abort(); } while (0)
-
-/**
- * log_err + exit failure
- */
-#define FATAL_ERR(err_code, ...) do { _log_err(LOG_FATAL, err_code, __func__, __VA_ARGS__); abort(); } while (0)
-
-/**
- * log_err_info + exit failure
- */
-#define FATAL_ERROR(err_info, ...) do { _log_error(LOG_FATAL, err_info, __func__, __VA_ARGS__); abort(); } while (0)
-
-/**
- * log_perr + exit failure
- */
-#define FATAL_PERROR(...) do { _log_perr(LOG_FATAL, __func__, __VA_ARGS__); abort(); } while (0)
-
-/**
- * Exit with given code, also logging a message at LOG_INFO with an [EXIT] tag
- */
-#define EXIT_INFO(exit_code, ...) log_exit(LOG_INFO, exit_code, __VA_ARGS__)
-#define EXIT_WARN(exit_code, ...) log_exit(LOG_WARN, exit_code, __VA_ARGS__)
-#define EXIT_ERROR(exit_code, ...) log_exit(LOG_ERROR, exit_code, __VA_ARGS__)
-#define EXIT_FATAL(exit_code, ...) log_exit(LOG_FATAL, exit_code, __VA_ARGS__)
-
-#endif /* LOG_H */
--- a/src/lua_config.c Wed May 27 23:07:00 2009 +0300
+++ b/src/lua_config.c Wed May 27 23:57:48 2009 +0300
@@ -2,7 +2,7 @@
#include <lua5.1/lauxlib.h>
-err_t lua_config_load (struct nexus_lua *lua, const char *path, struct error_info *err)
+err_t lua_config_load (struct nexus_lua *lua, const char *path, error_t *err)
{
// just use luaL_loadfile and translate the error code
if (nexus_lua_error(lua->st, luaL_loadfile(lua->st, path), err))
--- a/src/lua_config.h Wed May 27 23:07:00 2009 +0300
+++ b/src/lua_config.h Wed May 27 23:57:48 2009 +0300
@@ -14,6 +14,6 @@
*
* Path can also be given as NULL to load from stdin.
*/
-err_t lua_config_load (struct nexus_lua *lua, const char *path, struct error_info *err);
+err_t lua_config_load (struct nexus_lua *lua, const char *path, error_t *err);
#endif /* LUA_CONFIG_H */
--- a/src/lua_console.c Wed May 27 23:07:00 2009 +0300
+++ b/src/lua_console.c Wed May 27 23:57:48 2009 +0300
@@ -91,7 +91,7 @@
.on_interrupt = lua_console_on_interrupt,
};
-err_t lua_console_create (struct lua_console **lc_ptr, struct console *console, struct nexus_lua *lua, struct error_info *err)
+err_t lua_console_create (struct lua_console **lc_ptr, struct console *console, struct nexus_lua *lua, error_t *err)
{
struct lua_console *lc;
--- a/src/lua_console.h Wed May 27 23:07:00 2009 +0300
+++ b/src/lua_console.h Wed May 27 23:57:48 2009 +0300
@@ -21,7 +21,7 @@
*
* This overrides the console callbacks.
*/
-err_t lua_console_create (struct lua_console **lc_ptr, struct console *console, struct nexus_lua *lua, struct error_info *err);
+err_t lua_console_create (struct lua_console **lc_ptr, struct console *console, struct nexus_lua *lua, error_t *err);
/**
* Destroy the lua console state
--- a/src/module.c Wed May 27 23:07:00 2009 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,394 +0,0 @@
-#include "module.h"
-#include "log.h"
-
-#include <stdlib.h>
-#include <unistd.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;
-}
-
-const char* modules_path (struct modules *modules, const char *path)
-{
- const char *old_path = modules->path;
-
- if (path)
- // replace
- modules->path = path;
-
- return old_path;
-}
-
-/**
- * module_get without the refcount stuff
- */
-static struct module* modules_lookup (struct modules *modules, const char *name)
-{
- struct module *module = NULL;
-
- // look for it...
- TAILQ_FOREACH(module, &modules->list, modules_list) {
- if (strcasecmp(module->info.name, name) == 0) {
- // found it
- return module;
- }
- }
-
- // no such module
- return NULL;
-}
-
-struct module* modules_get (struct modules *modules, const char *name)
-{
- struct module *module;
-
- // get it
- if (!(module = modules_lookup(modules, name)))
- return NULL;
-
- // up the refcount
- module->refcount++;
-
- // ok
- return module;
-}
-
-err_t modules_unload (struct modules *modules)
-{
- struct module *module;
- err_t err;
-
- // unload each module in turn
- while ((module = TAILQ_FIRST(&modules->list))) {
- if ((err = module_unload(module)))
- log_warn("module_unload(%s) failed: %s", module->info.name, error_name(err));
- }
-
- // ok
- return SUCCESS;
-}
-
-void modules_destroy (struct modules *modules)
-{
- struct module *module;
-
- // destroy each module
- while ((module = TAILQ_FIRST(&modules->list)))
- module_destroy(module);
-
- // ourselves
- free(modules);
-}
-
-
-const char* module_name (struct module *module)
-{
- return module ? module->info.name : NULL;
-}
-
-/**
- * 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;
-}
-
-/**
- * XXX: ugly function to format to a dynamically allocated string
- */
-char *strfmt (const char *fmt, ...)
-{
- va_list vargs;
- size_t len;
- char *buf;
-
- // figure out the length of the resulting string
- va_start(vargs, fmt);
-
- len = vsnprintf(NULL, 0, fmt, vargs) + 1;
-
- va_end(vargs);
-
- // malloc
- if ((buf = malloc(len)) == NULL)
- return NULL;
-
- // format
- va_start(vargs, fmt);
-
- vsnprintf(buf, len, fmt, vargs);
-
- va_end(vargs);
-
- // ok
- return buf;
-}
-
-/**
- * Looks up a module's path by name, returning a dynamically allocated string with the path on success
- */
-static char* module_find (struct modules *modules, struct module_info *info, struct error_info *err)
-{
- char *path = NULL;
-
- // build the path...
- if ((path = strfmt("%s/mod_%s.so", modules->path, info->name)) == NULL)
- JUMP_SET_ERROR(err, ERR_STRDUP);
-
- // exists and readable?
- if (access(path, R_OK))
- // XXX: this doesn't contain the path...
- JUMP_SET_ERROR_STR(err, ERR_MODULE_PATH, "module not found in search path");
-
- // ok
- return path;
-
-error:
- // release the dynamically allocated path
- free(path);
-
- return NULL;
-}
-
-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);
-
- // already open with the same name?
- if (modules_lookup(modules, _info->name))
- return SET_ERROR(err, ERR_MODULE_DUP);
-
- // alloc
- if ((module = calloc(1, sizeof(*module))) == NULL)
- return SET_ERROR(err, ERR_CALLOC);
-
- // initialize
- module->info = *_info;
- module->modules = modules;
- module->refcount = 1;
-
- // add to modules list
- TAILQ_INSERT_TAIL(&modules->list, module, modules_list);
-
- // lookup path?
- if (!module->info.path) {
- // have a search path?
- if (!modules->path)
- JUMP_SET_ERROR_STR(err, ERR_MODULE_PATH, "no module search path defined");
-
- // try and resolve the path automatically
- if ((module->path_buf = module_find(modules, &module->info, err)) == NULL)
- goto error;
-
- // use that path
- module->info.path = module->path_buf;
- }
-
- // clear dlerrors
- (void) dlerror();
-
- // load it
- if ((module->handle = dlopen(module->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->desc, "module")))
- JUMP_SET_ERROR_STR(err, ERROR_CODE(err), dlerror());
-
- // call the init func
- if ((module->desc->init(modules->nexus, &module->ctx, err)))
- goto error;
-
- // ok
- if (module_ptr) {
- module->refcount++;
- *module_ptr = module;
- }
-
- return SUCCESS;
-
-error:
- // cleanup
- module->refcount = 0;
- module_destroy(module);
-
- return ERROR_CODE(err);
-}
-
-void module_put (struct module *module)
-{
- assert(module->refcount > 0);
-
- // decrement, just return if still alive
- if (--module->refcount > 0)
- return;
-
- // refcount zero, destroy
- module_destroy(module);
-}
-
-err_t module_conf_raw (struct module *module, const char *name, char *value, struct error_info *err)
-{
- // sanity-check
- if (!module->desc->config_options)
- RETURN_SET_ERROR_STR(err, ERR_MODULE_CONF, "module does not have any config options");
-
- // wrong state
- if (module->unloading)
- RETURN_SET_ERROR_STR(err, ERR_MODULE_CONF, "module is being unloaded");
-
- // parse/apply using the module's stuff
- return config_apply_raw(module->desc->config_options, module->modules->nexus, module->ctx, name, value, err);
-}
-
-const struct config_option* module_conf_lookup (struct module *module, const char *name, struct error_info *err)
-{
- // sanity-check
- if (!module->desc->config_options)
- JUMP_SET_ERROR_STR(err, ERR_MODULE_CONF, "module does not have any config options");
-
- // direct lookup
- return config_lookup(module->desc->config_options, name, err);
-
-error:
- return NULL;
-}
-
-err_t module_conf (struct module *module, const struct config_option *opt, const struct config_value *value, struct error_info *err)
-{
- // wrong state
- if (module->unloading)
- RETURN_SET_ERROR_STR(err, ERR_MODULE_CONF, "module is being unloaded");
-
- // apply with the module's ctx
- return config_apply_opt(opt, module->ctx, value, err);
-}
-
-err_t module_unload (struct module *module)
-{
- err_t err;
-
- // wrong state
- if (module->unloading)
- return ERR_MODULE_STATE;
-
- // remove from modules list
- TAILQ_REMOVE(&module->modules->list, module, modules_list);
-
- // update status
- module->unloading = true;
-
- if (module->desc->unload) {
- // invoke the unload func, passing it our reference
- // note that the module might not exist anymore after calling this
- if ((err = module->desc->unload(module->ctx, module))) {
- // mark it as "unloaded"
- module_unloaded(module);
-
- return err;
- }
-
- } else {
- // no unload procedure, just destroy it as soon as needed
- module_unloaded(module);
-
- }
- // ok
- return SUCCESS;
-}
-
-/**
- * A wrapper for module_destroy suitable for use as a libevent callback
- */
-static void _module_destroy (int fd, short what, void *arg)
-{
- struct module *module = arg;
-
- (void) fd;
- (void) what;
-
- // execute
- module_destroy(module);
-}
-
-void module_unloaded (struct module *module)
-{
- struct timeval tv = { 0, 0 };
- assert(module->refcount > 0);
-
- // decrement, just return if still alive
- if (--module->refcount > 0)
- return;
-
- // schedule a deferred module_destroy
- if (event_base_once(module->modules->nexus->ev_base, -1, EV_TIMEOUT, &_module_destroy, module, &tv))
- // XXX: how to reach?
- log_fatal("event_base_once failed, unable to schedule deferred module_destroy");
-}
-
-void module_destroy (struct module *module)
-{
- // XXX: warn about destroy with significant refcount...
- if (module->refcount)
- log_warn("destroying module %s with refcount>0: %zu", module_name(module), module->refcount);
- else
- log_debug("destroying module %s", module_name(module));
-
- // still need to remove from modules_list?
- if (!module->unloading)
- TAILQ_REMOVE(&module->modules->list, module, modules_list);
-
- // run the destroy hook
- if (module->desc && module->desc->destroy && module->ctx)
- module->desc->destroy(module->ctx);
-
- // unload the dl handle
- if (module->handle && dlclose(module->handle))
- log_warn("dlclose(%s): %s", module->info.name, dlerror());
-
- // release the path buf, if any
- free(module->path_buf);
-
- // free the module info
- free(module);
-}
-
-
--- a/src/module.h Wed May 27 23:07:00 2009 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,297 +0,0 @@
-#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.
- *
- * Modules are also reference counted, mainly for implementing module_unload(). When a module is first loaded, it has
- * a reference count of one - the entry in struct modules. Later, modules can be referenced using module_get(), and
- * returned with module_put() once done. The module should never be destroyed during its lifetime, until a
- * module_unload() occurs. At this point, the module is removed from the modules_list, and the one reference is
- * 'passed on' to the module itself. Once it has finished unloading, it can call module_unloaded() on the reference it was
- * given by module_desc::unload, which may then result in a deferred module_destroy().
- *
- * Module destroying itself is an interesting issue, since modules effectively need to be able to destroy themselves
- * (as they must be able to perform cleanup, and then notify completion from inside an event loop callback). This means
- * that they cannot directly execute a module_destroy() on themselves - if we call dlclose() with dlopen-mapped code
- * pages on the stack, a segfault ensues. Hence, they must call module_unloaded() on themselves, which then executes a
- * deferred module_destroy() if there are no references left. Otherwise, the module should be safe from external code,
- * as module_put() should never cause a module to be destroyed before module_unloaded() is executed, due to the primary
- * reference.
- *
- * Ugh, it's compliated, I need to write a clearer explenation once it's implemented :)
- */
-
-struct module;
-
-#include "nexus.h"
-#include "config.h"
-#include "error.h"
-
-#include <sys/queue.h>
-#include <stdbool.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 table of (function/other) pointers defining a module's behaviour. This is dynamically resolved from the module DSO
- * using the "<mod_name>_module" symbol.
- */
-struct module_desc {
- /**
- * 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.
- *
- * Implementing this is mandatory.
- *
- * @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);
-
- /**
- * A module may define a set of available configuration parameters for use by module_conf, by setting this to an
- * array of config_option's.
- *
- * The handler functions will recieve the module's context pointer as their ctx argument.
- *
- * Implementing this is optional, but recommended.
- */
- 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.
- *
- * The module given as an argument is the module itself - which has been removed from the modules_list - the
- * primary reference is passed on to the module. Once the module has finished unloading, it may call module_put(),
- * which may then call module_destroy(), if no other references were left.
- *
- * If the unload operation fails (returns an error code), then the module is considered as unloaded (as if
- * module_unloaded() was called - don't call this if you return an error).
- *
- * Implementing this is optional, if all of this can be implemented in destroy.
- *
- * @param ctx the module's context pointer as returned by init
- * @param module the hanging module reference, that must be passed to module_unloaded()
- * @return error code
- */
- err_t (*unload) (void *ctx, struct module *module);
-
- /**
- * Destroy the module now. No later chances, the module's code will be unloaded directly after this, which means
- * that attempts to execute the module's code (even on the stack...) after this will segfault.
- *
- * The module code /should/ garuntee that this is never called from *inside* the module code - calls to
- * module_destroy() will be deferred via the event loop if needed.
- *
- * Implementing this is optional.
- */
- void (*destroy) (void *ctx);
-};
-
-/**
- * A loaded module.
- */
-struct module {
- /** The identifying info for the module */
- struct module_info info;
-
- /** Possible dynamically allocated path */
- char *path_buf;
-
- /** The dlopen handle */
- void *handle;
-
- /** The module entry point */
- struct module_desc *desc;
-
- /** The module context object */
- void *ctx;
-
- /** Reference back to modules struct used to load this module */
- struct modules *modules;
-
- /** Reference count for destroy() */
- size_t refcount;
-
- /** Is the module currently being unloaded? */
- bool unloading;
-
- /** 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;
-
- /** Module search path */
- const char *path;
-
- /** 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_NAME, ///< invalid module_info.name
- ERR_MODULE_DUP, ///< module already opened
- ERR_MODULE_PATH, ///< resolving the path failed
- ERR_MODULE_OPEN, ///< dlopen() failed
- ERR_MODULE_SYM, ///< invalid symbol
- ERR_MODULE_INIT_FUNC, ///< invalid module_init_func_t
- ERR_MODULE_CONF, ///< value error in configuration data
- ERR_MODULE_STATE, ///< module in wrong state for operation
-};
-
-
-/**
- * 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);
-
-/**
- * Set a search path for finding modules by name. The given string won't be copied.
- *
- * A module called "<name>" will be searched for at "<path>/mod_<name>.so"
- *
- * If path is NULL, this doesn't change anything. This returns the old path, which may be NULL.
- *
- * @param modules the modules state
- * @param path the new search path, or NULL to just get the old one
- * @return the old search path
- */
-const char* modules_path (struct modules *modules, const char *path);
-
-/**
- * Get a reference to the module, which must be returned using module_put
- *
- * @param modules the modules state
- * @param name the module name to get
- * @return the module struct, or NULL if not found
- */
-struct module* modules_get (struct modules *modules, const char *name);
-
-/**
- * Unload all modules, this just calls module_unload for each module, logging errors as warnings.
- */
-err_t modules_unload (struct modules *modules);
-
-/**
- * Destroy all modules immediately.
- */
-void modules_destroy (struct modules *modules);
-
-
-
-/*******************************************/
-
-
-/**
- * Return a module's name
- */
-const char* module_name (struct module *module);
-
-/**
- * Load a new module, as named by info.
- *
- * If info->path is not given, the module will be searched for using the path set by modules_path().
- *
- * If module_ptr is given, a reference (that must be module_put'd) is returned, that must be returned using
- * module_put().
- *
- * @param modules the module-loading context
- * @param module_ptr retuturned new module struct, as a new reference, if not NULL.
- * @param info the info required to identify and load the module
- * @param err returned error info
- */
-err_t module_load (struct modules *modules, struct module **module_ptr, const struct module_info *info, struct error_info *err);
-
-/**
- * Return a module retrieved using module_get
- */
-void module_put (struct module *module);
-
-/**
- * Look up a module configuration option by name
- */
-const struct config_option* module_conf_lookup (struct module *module, const char *name, struct error_info *err);
-
-/**
- * Apply a module configuration option using a structured value
- */
-err_t module_conf (struct module *module, const struct config_option *opt, const struct config_value *value, struct error_info *err);
-
-/**
- * Set a module configuration option using a raw value
- */
-err_t module_conf_raw (struct module *module, const char *name, char *value, struct error_info *err);
-
-/**
- * Unload a module. This removes the module from the modules_list, marks it as unloading, and then calls the module's
- * \p unload function, passing it the primary reference. The module's unload code will then go about shutting down the
- * module, and once that is done, it may module_put() the primary reference, which may then lead to module_destroy().
- *
- * This returns ERR_MODULE_STATE if the module is already being unloaded, or other errors from the module's own unload
- * functionality.
- */
-err_t module_unload (struct module *module);
-
-/**
- * Used by a module itself to indicate that an module_desc::unload() operation has completed. This will execute a
- * deferred module_destroy() if there are no more references left on the module.
- *
- * Note: this is not intended to be called from outside the given module itself...
- */
-void module_unloaded (struct module *module);
-
-/**
- * Destroy a module, releasing as many resources as possible, but not stopping for errors.
- *
- * This does not enforce the correct refcount - 'tis the caller's responsibility. Prints out a warning if
- * refcount > 0.
- */
-void module_destroy (struct module *module);
-
-#endif
--- a/src/nexus.c Wed May 27 23:07:00 2009 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,592 +0,0 @@
-#include "nexus.h"
-#include "lua_config.h"
-#include "sock.h"
-#include "log.h"
-
-#include <stdlib.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <getopt.h>
-#include <signal.h>
-#include <string.h>
-
-/**
- * Command-line option codes
- */
-enum option_code {
- OPT_HELP = 'h',
- OPT_DEFAULTS = 'D',
- OPT_NETWORK = 'n',
- OPT_MODULE = 'm',
- OPT_CONF = 'C',
- OPT_DEBUG = 'd',
- OPT_CONFIG = 'c',
-
- /** Options without short names */
- _OPT_EXT_BEGIN = 0x00ff,
-
- OPT_CONSOLE,
- OPT_CHANNEL,
-};
-
-/**
- * Command-line option definitions
- */
-static struct option options[] = {
- {"help", 0, NULL, OPT_HELP },
- {"defaults", 1, NULL, OPT_DEFAULTS },
- {"network", 1, NULL, OPT_NETWORK },
- {"channel", 1, NULL, OPT_CHANNEL },
- {"module", 1, NULL, OPT_MODULE },
- {"conf", 1, NULL, OPT_CONF },
- {"debug", 0, NULL, OPT_DEBUG },
- {"console", 0, NULL, OPT_CONSOLE },
- {"config", 1, NULL, OPT_CONFIG },
- {0, 0, 0, 0 },
-};
-
-/**
- * Short command-line option defintion
- */
-const char *short_options = "hn:c:m:C:d";
-
-/**
- * Display --help output on stdout.
- *
- * If nexus is given, --config options for loaded modules are also listed
- */
-static void usage (struct nexus *nexus, const char *exe)
-{
- printf("Usage: %s [OPTIONS]\n", exe);
- printf("\n");
- printf(" --help / -h display this message\n");
- printf(" --defaults / -D set the IRC client default info using '<nickname>:<username>:<realname>'\n");
- printf(" --network / -n add an IRC network using '<name>:<hostname>[:<port>[:ssl][:ssl_cafile=<path>][:ssl_verify][:ssl_cert=<path>][:ssl_pkey=<path>]]' format\n");
- printf(" --channel add an IRC channel using '<network>:<channel>' format\n");
- 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");
- printf(" --config / -c load a Lua config file from '<path>'\n");
- printf(" --console use the interactive console\n");
-
- // dump loaded module configs
- 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);
-
- // XXX: shouldn't be accessing directly
- if (module->desc->config_options) {
- const struct config_option *opt;
-
- for (opt = module->desc->config_options; opt->name; opt++) {
- printf(" --config %s:%s:%s\t\t%s\n", module->info.name, opt->name, "xxx", opt->help);
- }
-
- } else {
- printf("\t???\n");
- }
- }
- }
-}
-
-/**
- * Parse and apply a --defaults option, setting the resulting defaults to our irc_client
- */
-static err_t apply_defaults (struct nexus *nexus, char *opt, struct error_info *err)
-{
- struct irc_client_defaults defaults = {
- .register_info = { NULL, NULL, NULL },
- .service = IRC_PORT,
- .service_ssl = IRC_SSL_PORT,
- };
-
- // parse the required fields
- if ((defaults.register_info.nickname = strsep(&opt, ":")) == NULL)
- RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "missing <nickname> field for --defaults");
-
- if ((defaults.register_info.username = strsep(&opt, ":")) == NULL)
- RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "missing <username> field for --defaults");
-
- if ((defaults.register_info.realname = strsep(&opt, ":")) == NULL)
- RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "missing <realname> field for --defaults");
-
- // trailing garbage?
- if (opt)
- RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "trailing values for --channel");
-
- // apply them
- log_info("using default nick/user/real-name: %s/%s/%s",
- defaults.register_info.nickname, defaults.register_info.username, defaults.register_info.realname);
-
- irc_client_set_defaults(nexus->client, &defaults);
-
- // ok
- return SET_ERROR(err, SUCCESS);
-}
-
-/**
- * Parse and apply a --network option, adding the given network to our irc_client.
- */
-static err_t apply_network (struct nexus *nexus, char *opt, struct error_info *err)
-{
- struct irc_net_info info;
- const char *ssl_cafile = NULL, *ssl_cert = NULL, *ssl_pkey = NULL;
- bool use_ssl = false, ssl_verify = false;
-
- // init to zero
- memset(&info, 0, sizeof(info));
-
- // parse the required fields
- if ((info.network = strsep(&opt, ":")) == NULL)
- JUMP_SET_ERROR_STR(err, ERR_CMD_OPT, "missing <name> field for --network");
-
- if ((info.hostname = strsep(&opt, ":")) == NULL)
- JUMP_SET_ERROR_STR(err, ERR_CMD_OPT, "missing <hostname> field for --network");
-
- // parse the optional fields
- if (opt)
- info.service = strsep(&opt, ":");
-
- // parse any remaining flags
- while (opt) {
- char *flag_token = strsep(&opt, ":");
- char *flag = strsep(&flag_token, "=");
- char *value = flag_token;
-
- if (strcmp(flag, "ssl") == 0) {
- use_ssl = true;
-
- } else if (strcmp(flag, "ssl_cafile") == 0) {
- if (!value)
- JUMP_SET_ERROR_STR(err, ERR_CMD_OPT, "missing value for :ssl_cafile");
-
- ssl_cafile = value;
-
- } else if (strcmp(flag, "ssl_verify") == 0) {
- ssl_verify = true;
-
- } else if (strcmp(flag, "ssl_cert") == 0) {
- if (!value)
- JUMP_SET_ERROR_STR(err, ERR_CMD_OPT, "missing value for :ssl_cert");
-
- ssl_cert = value;
-
- } else if (strcmp(flag, "ssl_pkey") == 0) {
- if (!value)
- JUMP_SET_ERROR_STR(err, ERR_CMD_OPT, "missing value for :ssl_pkey");
-
- ssl_pkey = value;
-
- } else
- JUMP_SET_ERROR_STR(err, ERR_CMD_OPT, "unrecognized flag for --network");
- }
-
- // SSL?
- if (use_ssl || ssl_cafile || ssl_verify || ssl_cert || ssl_pkey) {
- // verify
- if ((ssl_cert || ssl_pkey) && !(ssl_cert && ssl_pkey))
- JUMP_SET_ERROR_STR(err, ERR_CMD_OPT, "must give both :ssl_cert/:ssl_pkey");
-
- // create
- if (ssl_client_cred_create(&info.ssl_cred, ssl_cafile, ssl_verify, ssl_cert, ssl_pkey, err))
- goto error;
- }
-
- // create the net
- log_info("add network '%s' at '%s:%s'", info.network, info.hostname, info.service);
-
- if (irc_client_add_net(nexus->client, NULL, &info, err))
- goto error;
-
- // ok
- return SUCCESS;
-
-error:
- // cleanup
- if (info.ssl_cred)
- ssl_client_cred_put(info.ssl_cred);
-
- return ERROR_CODE(err);
-}
-
-/**
- * Parse and apply a --channel option, adding the given channel to the given irc_net.
- */
-static err_t apply_channel (struct nexus *nexus, char *opt, struct error_info *err)
-{
- const char *network = NULL;
- struct irc_net *net;
- struct irc_chan_info info = {
- .channel = NULL,
- };
-
- // parse the required fields
- if ((network = strsep(&opt, ":")) == NULL)
- RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "missing <network> field for --channel");
-
- if ((info.channel = strsep(&opt, ":")) == NULL)
- RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "missing <channel> field for --channel");
-
- // trailing garbage?
- if (opt)
- RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "trailing values for --channel");
-
- // look up the net
- if ((net = irc_client_get_net(nexus->client, network)) == NULL)
- RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "unknown network for --channel");
-
- // add the channel
- log_info("add channel '%s' on network '%s'", info.channel, net->info.network);
-
- if (irc_net_add_chan(net, NULL, &info, err))
- return ERROR_CODE(err);
-
- // ok
- return SUCCESS;
-}
-
-/**
- * Parse and apply a --module option, loading the given module.
- */
-static err_t apply_module (struct nexus *nexus, char *opt, struct error_info *err)
-{
- struct module_info info = {
- .name = NULL,
- .path = NULL,
- };
-
- // parse the required fields
- if ((info.name = strsep(&opt, ":")) == NULL)
- RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "missing <name> field for --module");
-
- if ((info.path = strsep(&opt, ":")) == NULL)
- RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "missing <path> field for --module");
-
- // trailing garbage?
- if (opt)
- RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "trailing values for --channel");
-
- // load it
- log_info("loading module '%s' from path '%s'", info.name, info.path);
-
- if (module_load(nexus->modules, NULL, &info, err))
- return ERROR_CODE(err);
-
- // ok
- return SUCCESS;
-}
-
-/**
- * Parse and apply a --conf option, calling the module's conf func.
- */
-static err_t apply_conf (struct nexus *nexus, char *opt, struct error_info *err)
-{
- struct module *module;
- const char *module_name, *conf_name;
- char *conf_value;
-
- // parse the required fields
- if ((module_name = strsep(&opt, ":")) == NULL)
- RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "missing <module> field for --config");
-
- if ((conf_name = strsep(&opt, ":")) == NULL)
- RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "missing <name> field for --config");
-
- // value is the rest of the data, might be NULL
- conf_value = opt;
-
- // lookup the module
- if ((module = modules_get(nexus->modules, module_name)) == NULL)
- RETURN_SET_ERROR_STR(err, ERR_CMD_OPT, "unknown module for --config");
-
- // do the config
- log_info("applying module '%s' config name '%s' with value: %s", module->info.name, conf_name, conf_value);
-
- if (module_conf_raw(module, conf_name, conf_value, err))
- return ERROR_CODE(err);
-
- // ok
- return SUCCESS;
-}
-
-/**
- * Open the console
- */
-static err_t apply_console (struct nexus *nexus, struct error_info *err)
-{
- struct console_config config = {
- .prompt = "> ",
- };
- struct console *console;
-
- log_info("initializing the console");
-
- // init the console
- if (console_init(&console, nexus->ev_base, &config, NULL, NULL, err))
- return ERROR_CODE(err);
-
- // set it as the log output handler
- console_set_log_output(console);
-
- // create the lua console on top of that
- if (lua_console_create(&nexus->lua_console, console, nexus->lua, err))
- goto error;
-
- // ok
- return SUCCESS;
-
-error:
- console_destroy(console);
-
- return ERROR_CODE(err);
-}
-
-err_t nexus_load_config (struct nexus *nexus, const char *path, struct error_info *err)
-{
- return lua_config_load(nexus->lua, path, err);
-}
-
-/**
- * Load the lua config file from \a path
- */
-static err_t apply_config (struct nexus *nexus, char *path, struct error_info *err)
-{
- log_info("loading lua config from %s", path);
-
- return nexus_load_config(nexus, path, err);
-}
-
-/**
- * Parse arguments and apply them to the given nexus
- */
-static err_t parse_args (struct nexus *nexus, int argc, char **argv, struct error_info *err)
-{
- int opt, option_index;
-
- // parse options
- while ((opt = getopt_long(argc, argv, short_options, options, &option_index)) != -1) {
- switch (opt) {
- case OPT_HELP:
- usage(nexus, argv[0]);
-
- // XXX: return instead
- exit(EXIT_SUCCESS);
-
- case OPT_NETWORK:
- if (apply_network(nexus, optarg, err))
- return ERROR_CODE(err);
-
- break;
-
- case OPT_DEFAULTS:
- if (apply_defaults(nexus, optarg, err))
- return ERROR_CODE(err);
-
- break;
-
- case OPT_CHANNEL:
- if (apply_channel(nexus, optarg, err))
- return ERROR_CODE(err);
-
- break;
-
- case OPT_MODULE:
- if (apply_module(nexus, optarg, err))
- return ERROR_CODE(err);
-
- break;
-
- case OPT_CONF:
- if (apply_conf(nexus, optarg, err))
- return ERROR_CODE(err);
-
- break;
-
- case OPT_DEBUG:
- set_log_level(LOG_DEBUG);
- break;
-
- case OPT_CONSOLE:
- if (apply_console(nexus, err))
- return ERROR_CODE(err);
-
- break;
-
- case OPT_CONFIG:
- if (apply_config(nexus, optarg, err))
- return ERROR_CODE(err);
-
- break;
-
- case '?':
- usage(nexus, argv[0]);
- return SET_ERROR(err, ERR_CMD_OPT);
- }
- }
-
- // ok
- return SUCCESS;
-}
-
-void nexus_shutdown (struct nexus *nexus)
-{
- // destroy the console
- if (nexus->lua_console)
- lua_console_destroy(nexus->lua_console);
-
- nexus->lua_console = NULL;
-
- // unload the modules
- if (nexus->modules)
- modules_unload(nexus->modules);
-
- // quit the irc client
- if (nexus->client)
- irc_client_quit(nexus->client, "Goodbye, cruel world ;(");
-
- // remove the signal handlers
- if (nexus->signals)
- signals_free(nexus->signals);
-
- nexus->signals = NULL;
-
- // now event_base_dispatch should return once everythings' shut down...
- nexus->shutdown = true;
-}
-
-void nexus_crash (struct nexus *nexus)
-{
- // force-quit the event loop
- event_base_loopbreak(nexus->ev_base);
-}
-
-void nexus_destroy (struct nexus *nexus)
-{
- // destroy the console
- if (nexus->lua_console)
- lua_console_destroy(nexus->lua_console);
-
- // destroy the lua state
- if (nexus->lua)
- nexus_lua_destroy(nexus->lua);
-
- // destroy the modules
- if (nexus->modules)
- modules_destroy(nexus->modules);
-
- // destroy the irc client
- if (nexus->client)
- irc_client_destroy(nexus->client);
-
- // remove the signal handlers
- if (nexus->signals)
- signals_free(nexus->signals);
-
- // finally, the libevent base
- if (nexus->ev_base)
- event_base_free(nexus->ev_base);
-
- // ok, nexus is now dead, so drop all pointers to make valgrind show things as leaked :)
- memset(nexus, 0, sizeof(*nexus));
-}
-
-static void on_sig_shutdown (evutil_socket_t sig, short what, void *arg)
-{
- struct nexus *nexus = arg;
-
- (void) sig;
- (void) what;
-
- if (!nexus->shutdown) {
- // shutdown
- log_info("Terminating...");
-
- nexus_shutdown(nexus);
-
- } else {
- // already tried to shutdown
- log_info("Crashing!");
-
- nexus_crash(nexus);
- }
-}
-
-int main (int argc, char **argv)
-{
- struct nexus _nexus, *nexus = &_nexus;
- struct error_info err;
-
- // zero nexus
- memset(nexus, 0, sizeof(*nexus));
-
- // initialize libevent
- if ((nexus->ev_base = event_base_new()) == NULL)
- FATAL("event_base_new");
-
-
- // initialize signal handlers
- if ((ERROR_CODE(&err) = signals_create(&nexus->signals, nexus->ev_base)))
- FATAL("signals_create");
-
- // add our signal handlers
- if (signal_ignore(SIGPIPE, &err))
- FATAL_ERROR(&err, "signals_ignore");
-
- // add our SIGTERM handler before console_init()?
- if (
- (ERROR_CODE(&err) = signals_add(nexus->signals, SIGTERM, on_sig_shutdown, nexus))
- || (ERROR_CODE(&err) = signals_add(nexus->signals, SIGINT, on_sig_shutdown, nexus))
- )
- FATAL_ERROR(&err, "signals_add");
-
- // initialize sock module
- if (sock_init(nexus->ev_base, &err))
- FATAL_ERROR(&err, "sock_init");
-
- // modules
- if ((ERROR_CODE(&err) = modules_create(&nexus->modules, nexus)))
- FATAL_ERROR(&err, "modules_create");
-
- // the IRC client
- if (irc_client_create(&nexus->client, &err))
- FATAL_ERROR(&err, "irc_client_create");
-
- // lua state
- if (nexus_lua_create(&nexus->lua, nexus, &err))
- FATAL_ERROR(&err, "nexus_lua_create");
-
-
- // parse args
- if (parse_args(nexus, argc, argv, &err))
- FATAL_ERROR(&err, "parse_args");
-
-
- // run event loop
- log_info("entering event loop");
-
- if (event_base_dispatch(nexus->ev_base)) {
- if (nexus->shutdown) {
- log_info("clean shutdown completed");
-
- } else {
- FATAL("event_base_dispatch returned without shutdown");
-
- }
- } else {
- log_warn("zero return from event_base_dispatch");
- }
-
- // cleanup
- nexus_destroy(nexus);
- nexus = NULL;
-
- // ok
- return EXIT_SUCCESS;
-}
-
--- a/src/nexus.h Wed May 27 23:07:00 2009 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,72 +0,0 @@
-#ifndef NEXUS_H
-#define NEXUS_H
-
-/**
- * @file
- *
- * A nexus is the central brain of the application; the place where the main() method is implemented
- */
-struct nexus;
-
-#include <event2/event.h>
-#include "signals.h"
-#include "module.h"
-#include "irc_client.h"
-#include "nexus_lua.h"
-#include "lua_console.h"
-
-/**
- * The central brain, as created in the main() function.
- */
-struct nexus {
- /** The libevent base */
- struct event_base *ev_base;
-
- /** Our signal handlers */
- struct signals *signals;
-
- /** Our loaded modules */
- struct modules *modules;
-
- /** The IRC client state */
- struct irc_client *client;
-
- /** Our lua state */
- struct nexus_lua *lua;
-
- /** Our lua console */
- struct lua_console *lua_console;
-
- /** Shutting down? */
- bool shutdown;
-};
-
-/**
- * Load a config file into the nexus from the given path
- */
-err_t nexus_load_config (struct nexus *nexus, const char *path, struct error_info *err);
-
-/**
- * Shut down the nexus cleanly. It /should/ be safe to call this several times, but it won't do anything.
- *
- * Once everything has shut down nicely (which it should, unless there's something buggy), the event loop should exit,
- * and the main() function return.
- */
-void nexus_shutdown (struct nexus *nexus);
-
-/**
- * Shut down the nexus fast. This will stop the event loop right away, which should lead to nexus_destroy being called.
- */
-void nexus_crash (struct nexus *nexus);
-
-/**
- * Destroy the nexus directly. This is intended to be used once the event loop as exited.
- */
-void nexus_destroy (struct nexus *nexus);
-
-/**
- * The nexus main function, application entry point, etc.
- */
-int main (int argc, char **argv);
-
-#endif /* NEXUS_H */
--- a/src/nexus_lua.c Wed May 27 23:07:00 2009 +0300
+++ b/src/nexus_lua.c Wed May 27 23:57:48 2009 +0300
@@ -31,7 +31,7 @@
return 0;
}
-err_t nexus_lua_create (struct nexus_lua **lua_ptr, struct nexus *nexus, struct error_info *err)
+err_t nexus_lua_create (struct nexus_lua **lua_ptr, struct nexus *nexus, error_t *err)
{
struct nexus_lua *lua;
@@ -69,7 +69,7 @@
free(lua);
}
-err_t nexus_lua_eval (struct nexus_lua *lua, const char *chunk, struct error_info *err)
+err_t nexus_lua_eval (struct nexus_lua *lua, const char *chunk, error_t *err)
{
int ret;
bool loaded = false;
@@ -99,7 +99,7 @@
return ERROR_CODE(err);
}
-err_t nexus_lua_error (lua_State *L, int ret, struct error_info *err)
+err_t nexus_lua_error (lua_State *L, int ret, error_t *err)
{
// XXX: this can raise an erorr itself
const char *error = lua_tostring(L, -1);
--- a/src/nexus_lua.h Wed May 27 23:07:00 2009 +0300
+++ b/src/nexus_lua.h Wed May 27 23:57:48 2009 +0300
@@ -26,7 +26,7 @@
/**
* Create a new lua state for nexus
*/
-err_t nexus_lua_create (struct nexus_lua **lua_ptr, struct nexus *nexus, struct error_info *err);
+err_t nexus_lua_create (struct nexus_lua **lua_ptr, struct nexus *nexus, error_t *err);
/**
* Destroy the lua state
--- a/src/object.c Wed May 27 23:07:00 2009 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-#include "object.h"
-
-#include <stdlib.h>
-#include <assert.h>
-
-void object_init (struct object *obj, const struct object_type *type)
-{
- // trip on bugs
- assert(!obj->type);
-
- // set type
- obj->type = type;
-}
-
-bool object_type_check (const struct object_type *obj_type, const struct object_type *type)
-{
- const struct object_type *t;
-
- // sanity-check
- assert(obj_type && type);
-
- // look for a matching type in the type's inheritance tree
- for (t = obj_type; t; t = t->parent)
- if (t == type)
- break;
-
- // t will be (parent == NULL) if we didn't find any matching type
- return (t != NULL);
-}
-
-bool object_check (struct object *obj, const struct object_type *type)
-{
- // sanity check
- assert(obj && type);
-
- return object_type_check(obj->type, type);
-}
-
-void* object_cast (struct object *obj, const struct object_type *type)
-{
- assert(object_type_check(obj->type, type));
-
- // ok, return as void*
- return obj;
-}
-
-const void* object_type (struct object *obj, const struct object_type *type)
-{
- assert(object_type_check(obj->type, type));
-
- // ok, return as void*
- return obj->type;
-}
--- a/src/object.h Wed May 27 23:07:00 2009 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,56 +0,0 @@
-#ifndef OBJECT_H
-#define OBJECT_H
-
-/**
- * @file
- *
- * The thing feared by every C-programmer - calling C structs "objects".
- */
-#include <stdbool.h>
-
-/**
- * Basic object type info
- */
-struct object_type {
- /** Parent type for nested structs, NULL if the parent is object_type */
- const struct object_type *parent;
-};
-
-/**
- * Basic object state
- */
-struct object {
- /** Object's type */
- const struct object_type *type;
-};
-
-/**
- * Initialize an object to the given type.
- */
-void object_init (struct object *obj, const struct object_type *type);
-
-/**
- * Check that the given object type is compatible with the given type.
- */
-bool object_type_check (const struct object_type *obj_type, const struct object_type *type);
-
-/**
- * Check that the given object "implements" the given type.
- */
-bool object_check (struct object *obj, const struct object_type *type);
-
-/**
- * Return the given object as a pointer suitable for casting to the struct for the given type.
- *
- * It is a bug to call this with an object that's not compatible with the given type.
- */
-void* object_cast (struct object *obj, const struct object_type *type);
-
-/**
- * Return a pointer to the object's type that is compatible with the given type.
- *
- * It is a bug to call this with a object that's not compatible with the given type.
- */
-const void* object_type (struct object *obj, const struct object_type *type);
-
-#endif /* OBJECT_H */
--- a/src/signals.c Wed May 27 23:07:00 2009 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,139 +0,0 @@
-
-#include "signals.h"
-#include "log.h"
-
-#define _GNU_SOURCE
-
-#include <string.h>
-#include <signal.h>
-#include <stdlib.h>
-#include <assert.h>
-
-struct signals {
- /** The libevent base to use */
- struct event_base *ev_base;
-
- /** Information about up to MAX_SIGNALS signals */
- struct signal {
- /** The event we use to handle the signal */
- struct event *ev;
-
- } sig_list[MAX_SIGNALS];
-
- /** Number of used sig_list slots */
- int sig_count;
-};
-
-void signals_loopexit (int signal, short event, void *arg)
-{
- struct signals *signals = arg;
-
- (void) event;
-
- log_info("caught %s: exiting", /* strsignal(signal) */ "xxx");
-
- if (event_base_loopexit(signals->ev_base, NULL))
- FATAL("event_base_loopexit");
-}
-
-void signals_ignore (int signal, short event, void *arg)
-{
- struct signals *signals = arg;
-
- (void) signal;
- (void) event;
- (void) arg;
- (void) signals;
-
- /* ignore */
-}
-
-err_t signals_create (struct signals **signals_ptr, struct event_base *ev_base)
-{
- struct signals *signals;
-
- if ((signals = calloc(1, sizeof(*signals))) == NULL)
- return ERR_CALLOC;
-
- // simple attributes
- signals->ev_base = ev_base;
-
- // done
- *signals_ptr = signals;
-
- return SUCCESS;
-}
-
-err_t signals_add (struct signals *signals, int sigval, void (*sig_handler)(evutil_socket_t, short, void *), void *arg)
-{
- struct signal *sig_info;
-
- // find our sig_info
- assert(signals->sig_count < MAX_SIGNALS);
- sig_info = signals->sig_list + (signals->sig_count++);
-
- // set up the libevent signal events
- if ((sig_info->ev = evsignal_new(signals->ev_base, sigval, sig_handler, arg)) == NULL)
- return ERR_EVENT_NEW;
-
- if (evsignal_add(sig_info->ev, NULL))
- return ERR_EVENT_ADD;
-
- // ok
- return SUCCESS;
-}
-
-err_t signal_ignore (int signum, struct error_info *err)
-{
- struct sigaction act = {
- .sa_handler = SIG_IGN,
- };
-
- // signall the handler
- if (sigaction(signum, &act, NULL))
- RETURN_SET_ERROR_ERRNO(err, ERR_SIGACTION);
-
- // ok
- return SUCCESS;
-}
-
-struct signals *signals_default (struct event_base *ev_base)
-{
- struct signals *signals = NULL;
- struct error_info err;
-
- // alloc signals
- if (signals_create(&signals, ev_base))
- return NULL;
-
- // add the set of default signals
- if (signal_ignore(SIGPIPE, &err))
- log_error(&err, "signal_ignore(SIGPIPE)");
-
- if ((ERROR_CODE(&err) = signals_add(signals, SIGINT, &signals_loopexit, signals))) {
- log_error(&err, "signals_add(SIGINT)");
- goto error;
- }
-
- // ok
- return signals;
-
-error:
- signals_free(signals);
-
- return NULL;
-}
-
-void signals_free (struct signals *signals)
-{
- int i;
-
- // free all events
- for (i = 0; i < signals->sig_count; i++) {
- event_free(signals->sig_list[i].ev);
- }
-
- // free the info itself
- free(signals);
-}
-
--- a/src/signals.h Wed May 27 23:07:00 2009 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,61 +0,0 @@
-#ifndef SIGNALS_H
-#define SIGNALS_H
-
-/**
- * @file
- *
- * Handle signals in a libevent way
- */
-#include "error.h"
-#include <event2/event.h>
-
-/**
- * How many signals we can define actions for
- */
-#define MAX_SIGNALS 8
-
-/**
- * State for a set of signals
- */
-struct signals;
-
-/**
- * Used as a handler for signals that should cause a loopexit.
- */
-void signals_loopexit (int signal, short event, void *arg);
-
-/**
- * Used to receive signals, but discard them, and continue what we were doing.
- */
-void signals_ignore (int signal, short event, void *arg);
-
-/**
- * Allocate a signals struct, acting on the given ev_base.
- *
- * Returns NULL on failure
- */
-err_t signals_create (struct signals **signals_ptr, struct event_base *ev_base);
-
-/**
- * Add a signal to be handled by the given signals struct with the given handler.
- */
-err_t signals_add (struct signals *signals, int sigval, void (*sig_handler)(evutil_socket_t, short, void *), void *arg);
-
-/**
- * Ignore the given sigval.
- */
-err_t signal_ignore (int signum, struct error_info *err);
-
-/**
- * Add a set of default signals
- * SIGPIPE signals_ignore
- * SIGINT signals_loopexit
- */
-struct signals *signals_default (struct event_base *ev_base);
-
-/**
- * Free the resources/handlers associated with the given signal handler set
- */
-void signals_free (struct signals *signals);
-
-#endif /* SIGNALS_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/spbot/config.c Wed May 27 23:57:48 2009 +0300
@@ -0,0 +1,268 @@
+#include "config.h"
+
+#include <string.h>
+#include <assert.h>
+
+const struct error_list config_errors = ERROR_LIST("config",
+ ERROR_TYPE_STRING( ERR_CONFIG_NAME, "unknown config option" ),
+ ERROR_TYPE( ERR_CONFIG_TYPE, "invalid config type" ),
+ ERROR_TYPE_STRING( ERR_CONFIG_REQUIRED, "missing required value" ),
+ ERROR_TYPE_STRING( ERR_CONFIG_VALUE, "invalid value" ),
+ ERROR_TYPE( ERR_CONFIG_PARAMS, "invalid number of paramters" )
+);
+
+const struct config_option* config_lookup (const struct config_option *options, const char *name, error_t *err)
+{
+ const struct config_option *option;
+
+ // find the matching config opt
+ for (option = options; option->name; option++) {
+ if (strcmp(option->name, name) == 0)
+ return option;
+ }
+
+ // not found
+ SET_ERROR_STR(err, &config_errors, ERR_CONFIG_NAME, name);
+
+ return NULL;
+}
+
+int config_params_count (const struct config_option *option)
+{
+ const struct config_param *param;
+ int count = 0;
+
+ // handle each param
+ for (param = option->params; param->name && param->type; param++)
+ count++;
+
+ return count;
+}
+
+/**
+ * Parse a raw value for a CONFIG_IRC_CHAN into an irc_chan
+ */
+static struct irc_chan* config_parse_irc_chan (struct nexus *nexus, char *raw_value, error_t *err)
+{
+ const char *network, *channel;
+ struct irc_chan *chan;
+
+ // XXX: wrong error code
+
+ // parse required args
+ if ((network = strsep(&raw_value, "/")) == NULL)
+ JUMP_SET_ERROR_STR(err, &config_errors, ERR_CONFIG_VALUE, "invalid <network> for CONFIG_IRC_CHAN value");
+
+ if ((channel = strsep(&raw_value, "/")) == NULL)
+ JUMP_SET_ERROR_STR(err, &config_errors, ERR_CONFIG_VALUE, "invalid <channel> for CONFIG_IRC_CHAN value");
+
+ // extraneous stuff?
+ if (raw_value)
+ JUMP_SET_ERROR_STR(err, &config_errors, ERR_CONFIG_VALUE, "trailing data for CONFIG_IRC_CHAN value");
+
+ // get the channel?
+ if ((chan = irc_client_get_chan(nexus->client, network, channel)) == NULL)
+ JUMP_SET_ERROR_STR(err, &config_errors, ERR_CONFIG_VALUE, "unknown network/channel name for CONFIG_IRC_CHAN value");
+
+ // ok
+ return chan;
+
+error:
+ return NULL;
+}
+
+err_t config_parse_param (const struct config_param *param, struct nexus *nexus, struct config_value *value, char *raw_value, error_t *err)
+{
+ // parse the value
+ switch (param->type) {
+ case CONFIG_INVALID:
+ return SET_ERROR_STR(err, &config_errors, ERR_CONFIG_TYPE, "invalid value for invalid type (too many values?)");
+
+ case CONFIG_STRING:
+ // simple!
+ value->string = raw_value;
+ break;
+
+ case CONFIG_IRC_CHAN:
+ // parse the value
+ if (!(value->irc_chan = config_parse_irc_chan(nexus, raw_value, err)))
+ return error_code(err);
+
+ break;
+
+ case CONFIG_USER:
+ // fail
+ return SET_ERROR_STR(err, &config_errors, ERR_CONFIG_TYPE, "user type can't be parsed");
+
+ default:
+ NOT_REACHED(param->type);
+ }
+
+ // copy the type
+ value->type = param->type;
+
+ // ok
+ return SUCCESS;
+}
+
+err_t config_parse (const struct config_option *option, struct nexus *nexus, struct config_value *value, char *raw_value, error_t *err)
+{
+ // use the first param
+ const struct config_param *param = &option->params[0];
+
+ // must have exactly one param
+ if (!param->type || (param + 1)->type)
+ return SET_ERROR(err, &config_errors, ERR_CONFIG_PARAMS);
+
+ // parse it
+ return config_parse_param(param, nexus, value, raw_value, err);
+}
+
+err_t config_apply_opt (const struct config_option *option, void *ctx, const struct config_value values[], error_t *err)
+{
+ const struct config_param *param;
+ const struct config_value *value;
+
+ // handle each param
+ for (param = option->params, value = values; param->name && param->type; param++) {
+ // no value given, or value given as NULL?
+ if (!value->type || value->type == CONFIG_NULL) {
+ if (param->optional) {
+ // optional, so just ignore the value
+
+ } else {
+ // required
+ JUMP_SET_ERROR_STR(err, &config_errors, ERR_CONFIG_REQUIRED, param->name);
+
+ }
+
+ } else if (param->type != value->type) {
+ // invalid type, XXX: also report correct type name?
+ JUMP_SET_ERROR(err, &config_errors, ERR_CONFIG_TYPE);
+
+ } else if (param->is_handler) {
+ // only applicable for non-optional args
+ err_t tmp;
+
+ // invoke the handler
+ switch (param->type) {
+ case CONFIG_STRING: tmp = param->func.string(ctx, value->string, err); break;
+ case CONFIG_IRC_CHAN: tmp = param->func.irc_chan(ctx, value->irc_chan, err); break;
+ default: NOT_REACHED(param->type);
+ }
+
+ // abort on errors
+ if (tmp)
+ goto error;
+ }
+
+ // the values list is NULL-terminated, and optional params can be left off
+ if (value->type)
+ value++;
+ }
+
+ // the option's handler?
+ if (option->func && option->func(ctx, option, values, err))
+ goto error;
+
+ // ok
+ return SUCCESS;
+
+error:
+ return error_code(err);
+}
+
+err_t config_apply (const struct config_option *options, void *ctx, const char *name, const struct config_value values[], error_t *err)
+{
+ const struct config_option *option;
+
+ // no matching option found?
+ if (!(option = config_lookup(options, name, err)))
+ return error_code(err);
+
+ // apply it
+ return config_apply_opt(option, ctx, values, err);
+}
+
+err_t config_apply_string (const struct config_option *options, void *ctx, const char *name, char *value, error_t *err)
+{
+ struct config_value conf_value[] = {
+ CONFIG_VALUE_STRING(value),
+ CONFIG_VALUE_END,
+ };
+
+ return config_apply(options, ctx, name, conf_value, err);
+}
+
+err_t config_apply_irc_chan (const struct config_option *options, void *ctx, const char *name, struct irc_chan *value, error_t *err)
+{
+ struct config_value conf_value[] = {
+ CONFIG_VALUE_IRC_CHAN(value),
+ CONFIG_VALUE_END,
+ };
+
+ return config_apply(options, ctx, name, conf_value, err);
+}
+
+err_t config_apply_raw (const struct config_option options[], struct nexus *nexus, void *ctx, const char *name, char *raw_value, error_t *err)
+{
+ const struct config_option *option;
+ struct config_value value[2] = {
+ CONFIG_VALUE_END,
+ CONFIG_VALUE_END,
+ };
+
+ // no matching option found?
+ if (!(option = config_lookup(options, name, err)))
+ return error_code(err);
+
+ // parse it
+ if (config_parse(option, nexus, &value[0], raw_value, err))
+ return error_code(err);
+
+ // apply it
+ return config_apply_opt(option, ctx, value, err);
+}
+
+const struct config_value* config_get_value (const struct config_option *option, const struct config_value values[], const char *name)
+{
+ const struct config_param *param;
+ const struct config_value *value;
+
+ // go through the (param, value) pairs
+ for (param = option->params, value = values; param->name && param->type && value->type; param++, value++) {
+ if (strcmp(param->name, name) == 0) {
+ // not NULL?
+ if (value->type != CONFIG_NULL)
+ return value;
+
+ else
+ return NULL;
+ }
+ }
+
+ // not found
+ return NULL;
+}
+
+const char* config_get_string (const struct config_option *option, const struct config_value values[], const char *name)
+{
+ const struct config_value *value;
+
+ return (value = config_get_value(option, values, name)) ? value->string : NULL;
+}
+
+struct irc_chan* config_get_irc_chan (const struct config_option *option, const struct config_value values[], const char *name)
+{
+ const struct config_value *value;
+
+ return (value = config_get_value(option, values, name)) ? value->irc_chan : NULL;
+}
+
+void* config_get_user (const struct config_option *option, const struct config_value values[], const char *name, const struct config_user_type *user_type)
+{
+ const struct config_value *value;
+
+ return ((value = config_get_value(option, values, name)) && value->user.type == user_type) ? value->user.ptr : NULL;
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/spbot/config.h Wed May 27 23:57:48 2009 +0300
@@ -0,0 +1,292 @@
+#ifndef CONFIG_H
+#define CONFIG_H
+
+/**
+ * @file
+ *
+ * Support for module configuration parameters
+ */
+#include <lib/error.h>
+#include <stdbool.h>
+
+enum config_error_code {
+ ERR_CONFIG_NONE,
+ ERR_CONFIG_NAME, ///< unknown option name
+ ERR_CONFIG_TYPE, ///< invalid value type for parameter
+ ERR_CONFIG_REQUIRED, ///< missing value for required parameter
+ ERR_CONFIG_VALUE, ///< invalid value
+ ERR_CONFIG_PARAMS, ///< invalid number of parameters
+};
+
+const struct error_list config_errors;
+
+/**
+ * Different types of configuration parameters
+ */
+enum config_type {
+ CONFIG_INVALID,
+
+ /** No value at all */
+ CONFIG_NULL,
+
+ /** A plain NUL-terminated string */
+ CONFIG_STRING,
+
+ /** A user-defined type, further identified by a string */
+ CONFIG_USER,
+
+ /** An IRC channel */
+ CONFIG_IRC_CHAN,
+};
+
+/**
+ * A CONFIG_USER type info
+ */
+struct config_user_type {
+ /** The name of the type */
+ const char *name;
+};
+
+/**
+ * Structure to hold a value as defined by config_type
+ */
+struct config_value {
+ /** The type of the value */
+ enum config_type type;
+
+ /** The typed value */
+ union {
+ /** Value for CONFIG_STRING */
+ char *string;
+
+ /** Value for CONFIG_IRC_CHAN */
+ struct irc_chan *irc_chan;
+
+ /** Value for CONFIG_USER */
+ struct {
+ /** The specific user type */
+ const struct config_user_type *type;
+
+ /** The pointer value */
+ void *ptr;
+ } user;
+ };
+};
+
+/**
+ * Structure to define a single parameter for an option
+ */
+struct config_param {
+ /** The name of the arg */
+ const char *name;
+
+ /** The type */
+ enum config_type type;
+
+ /** The specific type for CONFIG_USER */
+ const struct config_user_type *user_type;
+
+ /** Description */
+ const char *description;
+
+ /** Optional value handler function, by type */
+ union {
+ err_t (*string) (void *ctx, char *value, error_t *err);
+ err_t (*irc_chan) (void *ctx, struct irc_chan *chan, error_t *err);
+ } func;
+
+ /** Use handler function? */
+ bool is_handler;
+
+ /** Optional value? */
+ bool optional;
+};
+
+/**
+ * The maximum number of parameters that a single option can have, although this includes the terminating NULL
+ */
+#define CONFIG_PARAMS_MAX (15 + 1)
+
+/**
+ * The maximum number of values that an option can access, including the terminating NULL
+ */
+#define CONFIG_VALUES_MAX CONFIG_PARAMS_MAX
+
+/**
+ * A more complicated configuration option that can have multiple parameters
+ */
+struct config_option {
+ /** The name of the config option */
+ const char *name;
+
+ /** The list of parameters */
+ const struct config_param params[CONFIG_PARAMS_MAX];
+
+ /** The handler function */
+ err_t (*func) (void *ctx, const struct config_option *option, const struct config_value values[], error_t *err);
+
+ /** Help text */
+ const char *help;
+};
+
+#define CONFIG_PARAM(name, type, desc, optional) \
+ { name, type, NULL, desc, { NULL }, false, optional }
+
+#define CONFIG_PARAM_OPTIONAL(name, type, desc) \
+ CONFIG_PARAM(name, type, desc, true)
+
+#define CONFIG_PARAM_USER(name, user_type, desc, optional) \
+ { name, CONFIG_USER, user_type, desc, { NULL }, false, optional }
+
+#define CONFIG_PARAM_USER_OPTIONAL(name, user_type, desc) \
+ CONFIG_PARAM_USER(name, user_type, desc, true)
+
+#define CONFIG_PARAM_HANDLER(name, type, desc, _func_name_, func) \
+ { name, type, NULL, desc, {._func_name_ = func }, true, false }
+
+#define CONFIG_PARAM_END \
+ { NULL, 0, NULL, NULL, { NULL }, false, false }
+
+#define CONFIG_OPT(name, func, help, ...) \
+ { name, { __VA_ARGS__, CONFIG_PARAM_END }, func, help }
+
+#define CONFIG_OPT_STRING(name, func, desc, help) \
+ CONFIG_OPT(name, NULL, help, CONFIG_PARAM_HANDLER(name, CONFIG_STRING, desc, string, func))
+
+#define CONFIG_OPT_IRC_CHAN(name, func, desc, help) \
+ CONFIG_OPT(name, NULL, help, CONFIG_PARAM_HANDLER(name, CONFIG_IRC_CHAN, desc, irc_chan, func))
+
+#define CONFIG_OPT_END \
+ { NULL, {}, NULL, NULL }
+
+#define CONFIG_OPTIONS(...) \
+ { __VA_ARGS__, CONFIG_OPT_END }
+
+#define CONFIG_VALUE(type, _name_, value) \
+ { (type), { ._name_ = value } }
+
+#define CONFIG_VALUE_STRING(str_value) \
+ CONFIG_VALUE(CONFIG_STRING, string, (str_value))
+
+#define CONFIG_VALUE_IRC_CHAN(irc_chan_value) \
+ CONFIG_VALUE(CONFIG_IRC_CHAN, irc_chan, (irc_chan_value))
+
+#define CONFIG_VALUE_USER(user_type, user_value) \
+ CONFIG_VALUE(CONFIG_USER, user, { user_type, user_value })
+
+#define CONFIG_VALUE_END \
+ { 0, { NULL } }
+
+/**
+ * Lookup a config option by name.
+ *
+ * @param options a CONFIG_OPT_END-terminated array of config_option's
+ * @param name the config_option::name to look up
+ * @param err returned error info if not found
+ * @return a direct pointer to the config_option if found
+ */
+const struct config_option* config_lookup (const struct config_option *options, const char *name, error_t *err);
+
+/**
+ * Returns the number of params that an option has
+ *
+ * @param option the option to count
+ * @return the number of params
+ */
+int config_params_count (const struct config_option *option);
+
+// XXX: move this into nexus
+#include "nexus.h"
+/**
+ * Parse a raw value into a suitable configuration value for the given param, based on its type.
+ *
+ * Since this needs to access the application state, you need to pass in the nexus as an argument.
+ *
+ * Formats supported:
+ * CONFIG_IRC_CHAN - uses a '<network>/<channel>' format and irc_client_get_chan
+ *
+ */
+err_t config_parse_param (const struct config_param *param, struct nexus *nexus, struct config_value *value, char *raw_value, error_t *err);
+
+/**
+ * Parse a raw value into a suitable configuration value for a single-param option, based on the config option type.
+ *
+ * @param option the option to parse the value for, use config_lookup to find it
+ * @param nexus the application state
+ * @param value the returned value, if succesfull
+ * @param raw_value the raw value to parse based on the type
+ * @param err returned error info
+ */
+err_t config_parse (const struct config_option *option, struct nexus *nexus, struct config_value *value, char *raw_value, error_t *err);
+
+/**
+ * Apply a list of parsed configuration values to the given config_option struct.
+ *
+ * The config option handlers take a context argument; the value of this depends on the implementor of the config_option.
+ *
+ * @param option the option to apply
+ * @param ctx the context pointer for the option handler
+ * @param values the NULL-terminated array of parsed values
+ * @param err returned error info
+ */
+err_t config_apply_opt (const struct config_option *option, void *ctx, const struct config_value values[], error_t *err);
+
+/**
+ * Apply a list of parsed configuration values for the named config opt.
+ *
+ * @param options a CONFIG_OPT_END-terminated array of config_option's
+ * @param ctx the context pointer for the option handler
+ * @param name the config_option::name to look up
+ * @param values the NULL-termianted array of parsed values
+ * @param err returned error info
+ */
+err_t config_apply (const struct config_option *options, void *ctx, const char *name, const struct config_value values[], error_t *err);
+
+/**
+ * Apply a string value for the named config opt.
+ *
+ * @param options a CONFIG_OPT_END-terminated array of config_option's
+ * @param ctx the context pointer for the option handler
+ * @param name the config_option::name to look up
+ * @param value the string value
+ * @param err returned error info
+ */
+err_t config_apply_string (const struct config_option *options, void *ctx, const char *name, char *value, error_t *err);
+
+/**
+ * Apply an irc_chan value for the named config opt.
+ *
+ * @param options a CONFIG_OPT_END-terminated array of config_option's
+ * @param ctx the context pointer for the option handler
+ * @param name the config_option::name to look up
+ * @param value the irc_chan value
+ * @param err returned error info
+ */
+err_t config_apply_irc_chan (const struct config_option *options, void *ctx, const char *name, struct irc_chan *value, error_t *err);
+
+/**
+ * Parse and apply a configuration value for the named config opt.
+ *
+ * See config_parse() for more info.
+ *
+ * @param options a CONFIG_OPT_END-terminated array of config_option's
+ * @param nexus the application state
+ * @param ctx the context pointer for the option handler
+ * @param name the config_option::name to look up
+ * @param raw_value the raw value to parse
+ * @param err returned error info
+ *
+ * @see config_parse
+ */
+err_t config_apply_raw (const struct config_option option[], struct nexus *nexus, void *ctx, const char *name, char *raw_value, error_t *err);
+
+/**
+ * Lookup a config_value by name from the given list of config_values for the given config_option
+ */
+const struct config_value* config_get_value (const struct config_option *option, const struct config_value values[], const char *name);
+
+const char* config_get_string (const struct config_option *option, const struct config_value values[], const char *name);
+struct irc_chan* config_get_irc_chan (const struct config_option *option, const struct config_value values[], const char *name);
+void* config_get_user (const struct config_option *option, const struct config_value values[], const char *name, const struct config_user_type *user_type);
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/spbot/module.c Wed May 27 23:57:48 2009 +0300
@@ -0,0 +1,407 @@
+#include "module.h"
+#include "log.h"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <dlfcn.h>
+#include <string.h>
+#include <assert.h>
+
+const struct error_list module_errors = ERROR_LIST("module",
+ ERROR_TYPE_STRING( ERR_MODULE_NAME, "invalid module name" ),
+ ERROR_TYPE( ERR_MODULE_DUP, "module already loaded" ),
+ ERROR_TYPE_STRING( ERR_MODULE_PATH, "invalid module path" ),
+ ERROR_TYPE_STRING( ERR_MODULE_OPEN, "module dlopen() failed" ),
+ ERROR_TYPE_STRING( ERR_MODULE_SYM, "module dlsym() failed" ),
+ ERROR_TYPE_STRING( ERR_MODULE_INIT_FUNC, "invalid module init func" ),
+ ERROR_TYPE_STRING( ERR_MODULE_CONF, "module_conf" )
+);
+
+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;
+}
+
+const char* modules_path (struct modules *modules, const char *path)
+{
+ const char *old_path = modules->path;
+
+ if (path)
+ // replace
+ modules->path = path;
+
+ return old_path;
+}
+
+/**
+ * module_get without the refcount stuff
+ */
+static struct module* modules_lookup (struct modules *modules, const char *name)
+{
+ struct module *module = NULL;
+
+ // look for it...
+ TAILQ_FOREACH(module, &modules->list, modules_list) {
+ if (strcasecmp(module->info.name, name) == 0) {
+ // found it
+ return module;
+ }
+ }
+
+ // no such module
+ return NULL;
+}
+
+struct module* modules_get (struct modules *modules, const char *name)
+{
+ struct module *module;
+
+ // get it
+ if (!(module = modules_lookup(modules, name)))
+ return NULL;
+
+ // up the refcount
+ module->refcount++;
+
+ // ok
+ return module;
+}
+
+err_t modules_unload (struct modules *modules)
+{
+ struct module *module;
+ err_t err;
+
+ // unload each module in turn
+ while ((module = TAILQ_FIRST(&modules->list))) {
+ if ((err = module_unload(module)))
+ log_warn("module_unload(%s) failed: %s", module->info.name, error_name(err));
+ }
+
+ // ok
+ return SUCCESS;
+}
+
+void modules_destroy (struct modules *modules)
+{
+ struct module *module;
+
+ // destroy each module
+ while ((module = TAILQ_FIRST(&modules->list)))
+ module_destroy(module);
+
+ // ourselves
+ free(modules);
+}
+
+
+const char* module_name (struct module *module)
+{
+ return module ? module->info.name : NULL;
+}
+
+/**
+ * Load the symbol named "<module>_<suffix>".
+ */
+static err_t module_symbol (struct module *module, void **sym, const char *suffix, error_t *err)
+{
+ 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 SET_ERROR_STR(err, &module_errors, ERR_MODULE_SYM, sym_name);
+
+ // ok
+ return SUCCESS;
+}
+
+/**
+ * XXX: ugly function to format to a dynamically allocated string
+ */
+char *strfmt (const char *fmt, ...)
+{
+ va_list vargs;
+ size_t len;
+ char *buf;
+
+ // figure out the length of the resulting string
+ va_start(vargs, fmt);
+
+ len = vsnprintf(NULL, 0, fmt, vargs) + 1;
+
+ va_end(vargs);
+
+ // malloc
+ if ((buf = malloc(len)) == NULL)
+ return NULL;
+
+ // format
+ va_start(vargs, fmt);
+
+ vsnprintf(buf, len, fmt, vargs);
+
+ va_end(vargs);
+
+ // ok
+ return buf;
+}
+
+/**
+ * Looks up a module's path by name, returning a dynamically allocated string with the path on success
+ */
+static char* module_find (struct modules *modules, struct module_info *info, error_t *err)
+{
+ char *path = NULL;
+
+ // build the path...
+ if ((path = strfmt("%s/mod_%s.so", modules->path, info->name)) == NULL)
+ JUMP_SET_ERROR(err, &general_errors, ERR_MEM);
+
+ // exists and readable?
+ if (access(path, R_OK))
+ // XXX: this doesn't contain the path...
+ JUMP_SET_ERROR_STR(err, &module_errors, ERR_MODULE_PATH, "module not found in search path");
+
+ // ok
+ return path;
+
+error:
+ // release the dynamically allocated path
+ free(path);
+
+ return NULL;
+}
+
+err_t module_load (struct modules *modules, struct module **module_ptr, const struct module_info *_info, error_t *err)
+{
+ struct module *module;
+
+ // validate the module name
+ if (strlen(_info->name) > MODULE_NAME_MAX)
+ return SET_ERROR(err, &module_errors, ERR_MODULE_NAME);
+
+ // already open with the same name?
+ if (modules_lookup(modules, _info->name))
+ return SET_ERROR(err, &module_errors, ERR_MODULE_DUP);
+
+ // alloc
+ if ((module = calloc(1, sizeof(*module))) == NULL)
+ return SET_ERROR(err, &module_errors, ERR_CALLOC);
+
+ // initialize
+ module->info = *_info;
+ module->modules = modules;
+ module->refcount = 1;
+
+ // add to modules list
+ TAILQ_INSERT_TAIL(&modules->list, module, modules_list);
+
+ // lookup path?
+ if (!module->info.path) {
+ // have a search path?
+ if (!modules->path)
+ JUMP_SET_ERROR_STR(err, &module_errors, ERR_MODULE_PATH, "no module search path defined");
+
+ // try and resolve the path automatically
+ if ((module->path_buf = module_find(modules, &module->info, err)) == NULL)
+ goto error;
+
+ // use that path
+ module->info.path = module->path_buf;
+ }
+
+ // clear dlerrors
+ (void) dlerror();
+
+ // load it
+ if ((module->handle = dlopen(module->info.path, RTLD_NOW)) == NULL)
+ JUMP_SET_ERROR_STR(err, &module_errors, ERR_MODULE_OPEN, dlerror());
+
+ // load the funcs symbol
+ if ((ERROR_CODE(err) = module_symbol(module, (void **) &module->desc, "module")))
+ JUMP_SET_ERROR_STR(err, &module_errors, ERROR_CODE(err), dlerror());
+
+ // call the init func
+ if ((module->desc->init(modules->nexus, &module->ctx, err)))
+ goto error;
+
+ // ok
+ if (module_ptr) {
+ module->refcount++;
+ *module_ptr = module;
+ }
+
+ return SUCCESS;
+
+error:
+ // cleanup
+ module->refcount = 0;
+ module_destroy(module);
+
+ return ERROR_CODE(err);
+}
+
+void module_put (struct module *module)
+{
+ assert(module->refcount > 0);
+
+ // decrement, just return if still alive
+ if (--module->refcount > 0)
+ return;
+
+ // refcount zero, destroy
+ module_destroy(module);
+}
+
+err_t module_conf_raw (struct module *module, const char *name, char *value, error_t *err)
+{
+ // sanity-check
+ if (!module->desc->config_options)
+ RETURN_SET_ERROR_STR(err, &module_errors, ERR_MODULE_CONF, "module does not have any config options");
+
+ // wrong state
+ if (module->unloading)
+ RETURN_SET_ERROR_STR(err, &module_errors, ERR_MODULE_CONF, "module is being unloaded");
+
+ // parse/apply using the module's stuff
+ // XXX: error namespaces?
+ return config_apply_raw(module->desc->config_options, module->modules->nexus, module->ctx, name, value, err);
+}
+
+const struct config_option* module_conf_lookup (struct module *module, const char *name, error_t *err)
+{
+ // sanity-check
+ if (!module->desc->config_options)
+ JUMP_SET_ERROR_STR(err, &module_errors, ERR_MODULE_CONF, "module does not have any config options");
+
+ // direct lookup
+ // XXX: error namespaces?
+ return config_lookup(module->desc->config_options, name, err);
+
+error:
+ return NULL;
+}
+
+err_t module_conf (struct module *module, const struct config_option *opt, const struct config_value *value, error_t *err)
+{
+ // wrong state
+ if (module->unloading)
+ RETURN_SET_ERROR_STR(err, &module_errors, ERR_MODULE_CONF, "module is being unloaded");
+
+ // apply with the module's ctx
+ // XXX: error namespaces?
+ return config_apply_opt(opt, module->ctx, value, err);
+}
+
+err_t module_unload (struct module *module)
+{
+ err_t err;
+
+ // wrong state
+ if (module->unloading)
+ return ERR_MODULE_STATE;
+
+ // remove from modules list
+ TAILQ_REMOVE(&module->modules->list, module, modules_list);
+
+ // update status
+ module->unloading = true;
+
+ if (module->desc->unload) {
+ // invoke the unload func, passing it our reference
+ // note that the module might not exist anymore after calling this
+ if ((err = module->desc->unload(module->ctx, module))) {
+ // mark it as "unloaded"
+ module_unloaded(module);
+
+ return err;
+ }
+
+ } else {
+ // no unload procedure, just destroy it as soon as needed
+ module_unloaded(module);
+
+ }
+ // ok
+ return SUCCESS;
+}
+
+/**
+ * A wrapper for module_destroy suitable for use as a libevent callback
+ */
+static void _module_destroy (int fd, short what, void *arg)
+{
+ struct module *module = arg;
+
+ (void) fd;
+ (void) what;
+
+ // execute
+ module_destroy(module);
+}
+
+void module_unloaded (struct module *module)
+{
+ struct timeval tv = { 0, 0 };
+ assert(module->refcount > 0);
+
+ // decrement, just return if still alive
+ if (--module->refcount > 0)
+ return;
+
+ // schedule a deferred module_destroy
+ if (event_base_once(module->modules->nexus->ev_base, -1, EV_TIMEOUT, &_module_destroy, module, &tv))
+ // XXX: how to reach?
+ log_fatal("event_base_once failed, unable to schedule deferred module_destroy");
+}
+
+void module_destroy (struct module *module)
+{
+ // XXX: warn about destroy with significant refcount...
+ if (module->refcount)
+ log_warn("destroying module %s with refcount>0: %zu", module_name(module), module->refcount);
+ else
+ log_debug("destroying module %s", module_name(module));
+
+ // still need to remove from modules_list?
+ if (!module->unloading)
+ TAILQ_REMOVE(&module->modules->list, module, modules_list);
+
+ // run the destroy hook
+ if (module->desc && module->desc->destroy && module->ctx)
+ module->desc->destroy(module->ctx);
+
+ // unload the dl handle
+ if (module->handle && dlclose(module->handle))
+ log_warn("dlclose(%s): %s", module->info.name, dlerror());
+
+ // release the path buf, if any
+ free(module->path_buf);
+
+ // free the module info
+ free(module);
+}
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/spbot/module.h Wed May 27 23:57:48 2009 +0300
@@ -0,0 +1,295 @@
+#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.
+ *
+ * Modules are also reference counted, mainly for implementing module_unload(). When a module is first loaded, it has
+ * a reference count of one - the entry in struct modules. Later, modules can be referenced using module_get(), and
+ * returned with module_put() once done. The module should never be destroyed during its lifetime, until a
+ * module_unload() occurs. At this point, the module is removed from the modules_list, and the one reference is
+ * 'passed on' to the module itself. Once it has finished unloading, it can call module_unloaded() on the reference it was
+ * given by module_desc::unload, which may then result in a deferred module_destroy().
+ *
+ * Module destroying itself is an interesting issue, since modules effectively need to be able to destroy themselves
+ * (as they must be able to perform cleanup, and then notify completion from inside an event loop callback). This means
+ * that they cannot directly execute a module_destroy() on themselves - if we call dlclose() with dlopen-mapped code
+ * pages on the stack, a segfault ensues. Hence, they must call module_unloaded() on themselves, which then executes a
+ * deferred module_destroy() if there are no references left. Otherwise, the module should be safe from external code,
+ * as module_put() should never cause a module to be destroyed before module_unloaded() is executed, due to the primary
+ * reference.
+ *
+ * Ugh, it's compliated, I need to write a clearer explenation once it's implemented :)
+ */
+
+struct module;
+
+#include "nexus.h"
+#include "config.h"
+#include <lib/error.h>
+
+#include <sys/queue.h>
+#include <stdbool.h>
+
+enum module_error_code {
+ ERR_MODULE_NONE,
+ ERR_MODULE_NAME, ///< invalid module_info.name
+ ERR_MODULE_DUP, ///< module already opened
+ ERR_MODULE_PATH, ///< resolving the path failed
+ ERR_MODULE_OPEN, ///< dlopen() failed
+ ERR_MODULE_SYM, ///< invalid symbol
+ ERR_MODULE_INIT_FUNC, ///< invalid module_init_func_t
+ ERR_MODULE_CONF, ///< value error in configuration data
+ ERR_MODULE_STATE, ///< module in wrong state for operation
+};
+
+const struct error_list module_errors;
+
+/**
+ * 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 table of (function/other) pointers defining a module's behaviour. This is dynamically resolved from the module DSO
+ * using the "<mod_name>_module" symbol.
+ */
+struct module_desc {
+ /**
+ * 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.
+ *
+ * Implementing this is mandatory.
+ *
+ * @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, error_t *err);
+
+ /**
+ * A module may define a set of available configuration parameters for use by module_conf, by setting this to an
+ * array of config_option's.
+ *
+ * The handler functions will recieve the module's context pointer as their ctx argument.
+ *
+ * Implementing this is optional, but recommended.
+ */
+ 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.
+ *
+ * The module given as an argument is the module itself - which has been removed from the modules_list - the
+ * primary reference is passed on to the module. Once the module has finished unloading, it may call module_put(),
+ * which may then call module_destroy(), if no other references were left.
+ *
+ * If the unload operation fails (returns an error code), then the module is considered as unloaded (as if
+ * module_unloaded() was called - don't call this if you return an error).
+ *
+ * Implementing this is optional, if all of this can be implemented in destroy.
+ *
+ * @param ctx the module's context pointer as returned by init
+ * @param module the hanging module reference, that must be passed to module_unloaded()
+ * @return error code
+ */
+ err_t (*unload) (void *ctx, struct module *module);
+
+ /**
+ * Destroy the module now. No later chances, the module's code will be unloaded directly after this, which means
+ * that attempts to execute the module's code (even on the stack...) after this will segfault.
+ *
+ * The module code /should/ garuntee that this is never called from *inside* the module code - calls to
+ * module_destroy() will be deferred via the event loop if needed.
+ *
+ * Implementing this is optional.
+ */
+ void (*destroy) (void *ctx);
+};
+
+/**
+ * A loaded module.
+ */
+struct module {
+ /** The identifying info for the module */
+ struct module_info info;
+
+ /** Possible dynamically allocated path */
+ char *path_buf;
+
+ /** The dlopen handle */
+ void *handle;
+
+ /** The module entry point */
+ struct module_desc *desc;
+
+ /** The module context object */
+ void *ctx;
+
+ /** Reference back to modules struct used to load this module */
+ struct modules *modules;
+
+ /** Reference count for destroy() */
+ size_t refcount;
+
+ /** Is the module currently being unloaded? */
+ bool unloading;
+
+ /** 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;
+
+ /** Module search path */
+ const char *path;
+
+ /** List of loaded modules */
+ TAILQ_HEAD(module_ctx_modules, module) list;
+};
+
+
+/**
+ * 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);
+
+/**
+ * Set a search path for finding modules by name. The given string won't be copied.
+ *
+ * A module called "<name>" will be searched for at "<path>/mod_<name>.so"
+ *
+ * If path is NULL, this doesn't change anything. This returns the old path, which may be NULL.
+ *
+ * @param modules the modules state
+ * @param path the new search path, or NULL to just get the old one
+ * @return the old search path
+ */
+const char* modules_path (struct modules *modules, const char *path);
+
+/**
+ * Get a reference to the module, which must be returned using module_put
+ *
+ * @param modules the modules state
+ * @param name the module name to get
+ * @return the module struct, or NULL if not found
+ */
+struct module* modules_get (struct modules *modules, const char *name);
+
+/**
+ * Unload all modules, this just calls module_unload for each module, logging errors as warnings.
+ */
+err_t modules_unload (struct modules *modules);
+
+/**
+ * Destroy all modules immediately.
+ */
+void modules_destroy (struct modules *modules);
+
+
+
+/*******************************************/
+
+
+/**
+ * Return a module's name
+ */
+const char* module_name (struct module *module);
+
+/**
+ * Load a new module, as named by info.
+ *
+ * If info->path is not given, the module will be searched for using the path set by modules_path().
+ *
+ * If module_ptr is given, a reference (that must be module_put'd) is returned, that must be returned using
+ * module_put().
+ *
+ * @param modules the module-loading context
+ * @param module_ptr retuturned new module struct, as a new reference, if not NULL.
+ * @param info the info required to identify and load the module
+ * @param err returned error info
+ */
+err_t module_load (struct modules *modules, struct module **module_ptr, const struct module_info *info, error_t *err);
+
+/**
+ * Return a module retrieved using module_get
+ */
+void module_put (struct module *module);
+
+/**
+ * Look up a module configuration option by name
+ */
+const struct config_option* module_conf_lookup (struct module *module, const char *name, error_t *err);
+
+/**
+ * Apply a module configuration option using a structured value
+ */
+err_t module_conf (struct module *module, const struct config_option *opt, const struct config_value *value, error_t *err);
+
+/**
+ * Set a module configuration option using a raw value
+ */
+err_t module_conf_raw (struct module *module, const char *name, char *value, error_t *err);
+
+/**
+ * Unload a module. This removes the module from the modules_list, marks it as unloading, and then calls the module's
+ * \p unload function, passing it the primary reference. The module's unload code will then go about shutting down the
+ * module, and once that is done, it may module_put() the primary reference, which may then lead to module_destroy().
+ *
+ * This returns ERR_MODULE_STATE if the module is already being unloaded, or other errors from the module's own unload
+ * functionality.
+ */
+err_t module_unload (struct module *module);
+
+/**
+ * Used by a module itself to indicate that an module_desc::unload() operation has completed. This will execute a
+ * deferred module_destroy() if there are no more references left on the module.
+ *
+ * Note: this is not intended to be called from outside the given module itself...
+ */
+void module_unloaded (struct module *module);
+
+/**
+ * Destroy a module, releasing as many resources as possible, but not stopping for errors.
+ *
+ * This does not enforce the correct refcount - 'tis the caller's responsibility. Prints out a warning if
+ * refcount > 0.
+ */
+void module_destroy (struct module *module);
+
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/spbot/nexus.c Wed May 27 23:57:48 2009 +0300
@@ -0,0 +1,596 @@
+#include "nexus.h"
+#include "lua_config.h"
+#include "sock.h"
+#include <lib/log.h>
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <getopt.h>
+#include <signal.h>
+#include <string.h>
+
+/**
+ * Command-line option codes
+ */
+enum option_code {
+ OPT_HELP = 'h',
+ OPT_DEFAULTS = 'D',
+ OPT_NETWORK = 'n',
+ OPT_MODULE = 'm',
+ OPT_CONF = 'C',
+ OPT_DEBUG = 'd',
+ OPT_CONFIG = 'c',
+
+ /** Options without short names */
+ _OPT_EXT_BEGIN = 0x00ff,
+
+ OPT_CONSOLE,
+ OPT_CHANNEL,
+};
+
+/**
+ * Command-line option definitions
+ */
+static struct option options[] = {
+ {"help", 0, NULL, OPT_HELP },
+ {"defaults", 1, NULL, OPT_DEFAULTS },
+ {"network", 1, NULL, OPT_NETWORK },
+ {"channel", 1, NULL, OPT_CHANNEL },
+ {"module", 1, NULL, OPT_MODULE },
+ {"conf", 1, NULL, OPT_CONF },
+ {"debug", 0, NULL, OPT_DEBUG },
+ {"console", 0, NULL, OPT_CONSOLE },
+ {"config", 1, NULL, OPT_CONFIG },
+ {0, 0, 0, 0 },
+};
+
+/**
+ * Short command-line option defintion
+ */
+const char *short_options = "hn:c:m:C:d";
+
+/**
+ * Display --help output on stdout.
+ *
+ * If nexus is given, --config options for loaded modules are also listed
+ */
+static void usage (struct nexus *nexus, const char *exe)
+{
+ printf("Usage: %s [OPTIONS]\n", exe);
+ printf("\n");
+ printf(" --help / -h display this message\n");
+ printf(" --defaults / -D set the IRC client default info using '<nickname>:<username>:<realname>'\n");
+ printf(" --network / -n add an IRC network using '<name>:<hostname>[:<port>[:ssl][:ssl_cafile=<path>][:ssl_verify][:ssl_cert=<path>][:ssl_pkey=<path>]]' format\n");
+ printf(" --channel add an IRC channel using '<network>:<channel>' format\n");
+ 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");
+ printf(" --config / -c load a Lua config file from '<path>'\n");
+ printf(" --console use the interactive console\n");
+
+ // dump loaded module configs
+ 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);
+
+ // XXX: shouldn't be accessing directly
+ if (module->desc->config_options) {
+ const struct config_option *opt;
+
+ for (opt = module->desc->config_options; opt->name; opt++) {
+ printf(" --config %s:%s:%s\t\t%s\n", module->info.name, opt->name, "xxx", opt->help);
+ }
+
+ } else {
+ printf("\t???\n");
+ }
+ }
+ }
+}
+
+/**
+ * Parse and apply a --defaults option, setting the resulting defaults to our irc_client
+ */
+static err_t apply_defaults (struct nexus *nexus, char *opt, error_t *err)
+{
+ struct irc_client_defaults defaults = {
+ .register_info = { NULL, NULL, NULL },
+ .service = IRC_PORT,
+ .service_ssl = IRC_SSL_PORT,
+ };
+
+ // parse the required fields
+ if ((defaults.register_info.nickname = strsep(&opt, ":")) == NULL)
+ return SET_ERROR_STR(err, &general_errors, ERR_CMD_OPT, "missing <nickname> field for --defaults");
+
+ if ((defaults.register_info.username = strsep(&opt, ":")) == NULL)
+ return SET_ERROR_STR(err, &general_errors, ERR_CMD_OPT, "missing <username> field for --defaults");
+
+ if ((defaults.register_info.realname = strsep(&opt, ":")) == NULL)
+ return SET_ERROR_STR(err, &general_errors, ERR_CMD_OPT, "missing <realname> field for --defaults");
+
+ // trailing garbage?
+ if (opt)
+ return SET_ERROR_STR(err, &general_errors, ERR_CMD_OPT, "trailing values for --channel");
+
+ // apply them
+ log_info("using default nick/user/real-name: %s/%s/%s",
+ defaults.register_info.nickname, defaults.register_info.username, defaults.register_info.realname);
+
+ irc_client_set_defaults(nexus->client, &defaults);
+
+ // ok
+ return SUCCESS;
+}
+
+/**
+ * Parse and apply a --network option, adding the given network to our irc_client.
+ */
+static err_t apply_network (struct nexus *nexus, char *opt, error_t *err)
+{
+ struct irc_net_info info;
+ const char *ssl_cafile = NULL, *ssl_cert = NULL, *ssl_pkey = NULL;
+ bool use_ssl = false, ssl_verify = false;
+
+ // init to zero
+ memset(&info, 0, sizeof(info));
+
+ // parse the required fields
+ if ((info.network = strsep(&opt, ":")) == NULL)
+ JUMP_SET_ERROR_STR(err, &general_errors, ERR_CMD_OPT, "missing <name> field for --network");
+
+ if ((info.hostname = strsep(&opt, ":")) == NULL)
+ JUMP_SET_ERROR_STR(err, &general_errors, ERR_CMD_OPT, "missing <hostname> field for --network");
+
+ // parse the optional fields
+ if (opt)
+ info.service = strsep(&opt, ":");
+
+ // parse any remaining flags
+ while (opt) {
+ char *flag_token = strsep(&opt, ":");
+ char *flag = strsep(&flag_token, "=");
+ char *value = flag_token;
+
+ if (strcmp(flag, "ssl") == 0) {
+ use_ssl = true;
+
+ } else if (strcmp(flag, "ssl_cafile") == 0) {
+ if (!value)
+ JUMP_SET_ERROR_STR(err, &general_errors, ERR_CMD_OPT, "missing value for :ssl_cafile");
+
+ ssl_cafile = value;
+
+ } else if (strcmp(flag, "ssl_verify") == 0) {
+ ssl_verify = true;
+
+ } else if (strcmp(flag, "ssl_cert") == 0) {
+ if (!value)
+ JUMP_SET_ERROR_STR(err, &general_errors, ERR_CMD_OPT, "missing value for :ssl_cert");
+
+ ssl_cert = value;
+
+ } else if (strcmp(flag, "ssl_pkey") == 0) {
+ if (!value)
+ JUMP_SET_ERROR_STR(err, &general_errors, ERR_CMD_OPT, "missing value for :ssl_pkey");
+
+ ssl_pkey = value;
+
+ } else
+ JUMP_SET_ERROR_STR(err, &general_errors, ERR_CMD_OPT, "unrecognized flag for --network");
+ }
+
+ // SSL?
+ if (use_ssl || ssl_cafile || ssl_verify || ssl_cert || ssl_pkey) {
+ // verify
+ if ((ssl_cert || ssl_pkey) && !(ssl_cert && ssl_pkey))
+ JUMP_SET_ERROR_STR(err, &general_errors, ERR_CMD_OPT, "must give both :ssl_cert/:ssl_pkey");
+
+ // create
+ if (ssl_client_cred_create(&info.ssl_cred, ssl_cafile, ssl_verify, ssl_cert, ssl_pkey, err))
+ goto error;
+ }
+
+ // create the net
+ log_info("add network '%s' at '%s:%s'", info.network, info.hostname, info.service);
+
+ if (irc_client_add_net(nexus->client, NULL, &info, err))
+ goto error;
+
+ // ok
+ return SUCCESS;
+
+error:
+ // cleanup
+ if (info.ssl_cred)
+ ssl_client_cred_put(info.ssl_cred);
+
+ return ERROR_CODE(err);
+}
+
+/**
+ * Parse and apply a --channel option, adding the given channel to the given irc_net.
+ */
+static err_t apply_channel (struct nexus *nexus, char *opt, error_t *err)
+{
+ const char *network = NULL;
+ struct irc_net *net;
+ struct irc_chan_info info = {
+ .channel = NULL,
+ };
+
+ // parse the required fields
+ if ((network = strsep(&opt, ":")) == NULL)
+ return SET_ERROR_STR(err, &general_errors, ERR_CMD_OPT, "missing <network> field for --channel");
+
+ if ((info.channel = strsep(&opt, ":")) == NULL)
+ return SET_ERROR_STR(err, &general_errors, ERR_CMD_OPT, "missing <channel> field for --channel");
+
+ // trailing garbage?
+ if (opt)
+ return SET_ERROR_STR(err, &general_errors, ERR_CMD_OPT, "trailing values for --channel");
+
+ // look up the net
+ if ((net = irc_client_get_net(nexus->client, network)) == NULL)
+ return SET_ERROR_STR(err, &general_errors, ERR_CMD_OPT, "unknown network for --channel");
+
+ // add the channel
+ log_info("add channel '%s' on network '%s'", info.channel, net->info.network);
+
+ if (irc_net_add_chan(net, NULL, &info, err))
+ return ERROR_CODE(err);
+
+ // ok
+ return SUCCESS;
+}
+
+/**
+ * Parse and apply a --module option, loading the given module.
+ */
+static err_t apply_module (struct nexus *nexus, char *opt, error_t *err)
+{
+ struct module_info info = {
+ .name = NULL,
+ .path = NULL,
+ };
+
+ // parse the required fields
+ if ((info.name = strsep(&opt, ":")) == NULL)
+ return SET_ERROR_STR(err, &general_errors, ERR_CMD_OPT, "missing <name> field for --module");
+
+ if ((info.path = strsep(&opt, ":")) == NULL)
+ return SET_ERROR_STR(err, &general_errors, ERR_CMD_OPT, "missing <path> field for --module");
+
+ // trailing garbage?
+ if (opt)
+ return SET_ERROR_STR(err, &general_errors, ERR_CMD_OPT, "trailing values for --channel");
+
+ // load it
+ log_info("loading module '%s' from path '%s'", info.name, info.path);
+
+ if (module_load(nexus->modules, NULL, &info, err))
+ return ERROR_CODE(err);
+
+ // ok
+ return SUCCESS;
+}
+
+/**
+ * Parse and apply a --conf option, calling the module's conf func.
+ */
+static err_t apply_conf (struct nexus *nexus, char *opt, error_t *err)
+{
+ struct module *module;
+ const char *module_name, *conf_name;
+ char *conf_value;
+
+ // parse the required fields
+ if ((module_name = strsep(&opt, ":")) == NULL)
+ return SET_ERROR_STR(err, &general_errors, ERR_CMD_OPT, "missing <module> field for --config");
+
+ if ((conf_name = strsep(&opt, ":")) == NULL)
+ return SET_ERROR_STR(err, &general_errors, ERR_CMD_OPT, "missing <name> field for --config");
+
+ // value is the rest of the data, might be NULL
+ conf_value = opt;
+
+ // lookup the module
+ if ((module = modules_get(nexus->modules, module_name)) == NULL)
+ return SET_ERROR_STR(err, &general_errors, ERR_CMD_OPT, "unknown module for --config");
+
+ // do the config
+ log_info("applying module '%s' config name '%s' with value: %s", module->info.name, conf_name, conf_value);
+
+ if (module_conf_raw(module, conf_name, conf_value, err))
+ return ERROR_CODE(err);
+
+ // ok
+ return SUCCESS;
+}
+
+/**
+ * Open the console
+ */
+static err_t apply_console (struct nexus *nexus, error_t *err)
+{
+ struct console_config config = {
+ .prompt = "> ",
+ };
+ struct console *console;
+
+ log_info("initializing the console");
+
+ // init the console
+ if (console_init(&console, nexus->ev_base, &config, NULL, NULL, err))
+ return ERROR_CODE(err);
+
+ // set it as the log output handler
+ console_set_log_output(console);
+
+ // create the lua console on top of that
+ if (lua_console_create(&nexus->lua_console, console, nexus->lua, err))
+ goto error;
+
+ // ok
+ return SUCCESS;
+
+error:
+ console_destroy(console);
+
+ return ERROR_CODE(err);
+}
+
+err_t nexus_load_config (struct nexus *nexus, const char *path, error_t *err)
+{
+ return lua_config_load(nexus->lua, path, err);
+}
+
+/**
+ * Load the lua config file from \a path
+ */
+static err_t apply_config (struct nexus *nexus, char *path, error_t *err)
+{
+ log_info("loading lua config from %s", path);
+
+ return nexus_load_config(nexus, path, err);
+}
+
+/**
+ * Parse arguments and apply them to the given nexus
+ */
+static err_t parse_args (struct nexus *nexus, int argc, char **argv, error_t *err)
+{
+ int opt, option_index;
+
+ // parse options
+ while ((opt = getopt_long(argc, argv, short_options, options, &option_index)) != -1) {
+ switch (opt) {
+ case OPT_HELP:
+ usage(nexus, argv[0]);
+
+ // XXX: return instead
+ exit(EXIT_SUCCESS);
+
+ // XXX: come up with something nicer for parsing these command-line options
+ case OPT_NETWORK:
+ if (apply_network(nexus, optarg, err))
+ goto error;
+
+ break;
+
+ case OPT_DEFAULTS:
+ if (apply_defaults(nexus, optarg, err))
+ goto error;
+
+ break;
+
+ case OPT_CHANNEL:
+ if (apply_channel(nexus, optarg, err))
+ goto error;
+
+ break;
+
+ case OPT_MODULE:
+ if (apply_module(nexus, optarg, err))
+ goto error;
+
+ break;
+
+ case OPT_CONF:
+ if (apply_conf(nexus, optarg, err))
+ goto error;
+
+ break;
+
+ case OPT_DEBUG:
+ set_log_level(LOG_DEBUG);
+ break;
+
+ case OPT_CONSOLE:
+ if (apply_console(nexus, err))
+ goto error;
+
+ break;
+
+ case OPT_CONFIG:
+ if (apply_config(nexus, optarg, err))
+ goto error;
+
+ break;
+
+ case '?':
+ usage(nexus, argv[0]);
+ return SET_ERROR(err, &general_errors, ERR_CMD_OPT);
+ }
+ }
+
+ // ok
+ return SUCCESS;
+
+error:
+ return ERROR_CODE(err);
+}
+
+void nexus_shutdown (struct nexus *nexus)
+{
+ // destroy the console
+ if (nexus->lua_console)
+ lua_console_destroy(nexus->lua_console);
+
+ nexus->lua_console = NULL;
+
+ // unload the modules
+ if (nexus->modules)
+ modules_unload(nexus->modules);
+
+ // quit the irc client
+ if (nexus->client)
+ irc_client_quit(nexus->client, "Goodbye, cruel world ;(");
+
+ // remove the signal handlers
+ if (nexus->signals)
+ signals_free(nexus->signals);
+
+ nexus->signals = NULL;
+
+ // now event_base_dispatch should return once everythings' shut down...
+ nexus->shutdown = true;
+}
+
+void nexus_crash (struct nexus *nexus)
+{
+ // force-quit the event loop
+ event_base_loopbreak(nexus->ev_base);
+}
+
+void nexus_destroy (struct nexus *nexus)
+{
+ // destroy the console
+ if (nexus->lua_console)
+ lua_console_destroy(nexus->lua_console);
+
+ // destroy the lua state
+ if (nexus->lua)
+ nexus_lua_destroy(nexus->lua);
+
+ // destroy the modules
+ if (nexus->modules)
+ modules_destroy(nexus->modules);
+
+ // destroy the irc client
+ if (nexus->client)
+ irc_client_destroy(nexus->client);
+
+ // remove the signal handlers
+ if (nexus->signals)
+ signals_free(nexus->signals);
+
+ // finally, the libevent base
+ if (nexus->ev_base)
+ event_base_free(nexus->ev_base);
+
+ // ok, nexus is now dead, so drop all pointers to make valgrind show things as leaked :)
+ memset(nexus, 0, sizeof(*nexus));
+}
+
+static void on_sig_shutdown (evutil_socket_t sig, short what, void *arg)
+{
+ struct nexus *nexus = arg;
+
+ (void) sig;
+ (void) what;
+
+ if (!nexus->shutdown) {
+ // shutdown
+ log_info("Terminating...");
+
+ nexus_shutdown(nexus);
+
+ } else {
+ // already tried to shutdown
+ log_info("Crashing!");
+
+ nexus_crash(nexus);
+ }
+}
+
+int main (int argc, char **argv)
+{
+ struct nexus _nexus, *nexus = &_nexus;
+ error_t err;
+
+ // zero nexus
+ memset(nexus, 0, sizeof(*nexus));
+
+ // initialize libevent
+ if ((nexus->ev_base = event_base_new()) == NULL)
+ FATAL("event_base_new");
+
+
+ // initialize signal handlers
+ if ((ERROR_CODE(&err) = signals_create(&nexus->signals, nexus->ev_base)))
+ FATAL("signals_create");
+
+ // add our signal handlers
+ if (signal_ignore(SIGPIPE, &err))
+ FATAL_ERROR(&err, "signals_ignore");
+
+ // add our SIGTERM handler before console_init()?
+ if (
+ (ERROR_CODE(&err) = signals_add(nexus->signals, SIGTERM, on_sig_shutdown, nexus))
+ || (ERROR_CODE(&err) = signals_add(nexus->signals, SIGINT, on_sig_shutdown, nexus))
+ )
+ FATAL_ERROR(&err, "signals_add");
+
+ // initialize sock module
+ if (sock_init(nexus->ev_base, &err))
+ FATAL_ERROR(&err, "sock_init");
+
+ // modules
+ if ((ERROR_CODE(&err) = modules_create(&nexus->modules, nexus)))
+ FATAL_ERROR(&err, "modules_create");
+
+ // the IRC client
+ if (irc_client_create(&nexus->client, &err))
+ FATAL_ERROR(&err, "irc_client_create");
+
+ // lua state
+ if (nexus_lua_create(&nexus->lua, nexus, &err))
+ FATAL_ERROR(&err, "nexus_lua_create");
+
+
+ // parse args
+ if (parse_args(nexus, argc, argv, &err))
+ FATAL_ERROR(&err, "parse_args");
+
+
+ // run event loop
+ log_info("entering event loop");
+
+ if (event_base_dispatch(nexus->ev_base)) {
+ if (nexus->shutdown) {
+ log_info("clean shutdown completed");
+
+ } else {
+ FATAL("event_base_dispatch returned without shutdown");
+
+ }
+ } else {
+ log_warn("zero return from event_base_dispatch");
+ }
+
+ // cleanup
+ nexus_destroy(nexus);
+ nexus = NULL;
+
+ // ok
+ return EXIT_SUCCESS;
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/spbot/nexus.h Wed May 27 23:57:48 2009 +0300
@@ -0,0 +1,72 @@
+#ifndef NEXUS_H
+#define NEXUS_H
+
+/**
+ * @file
+ *
+ * A nexus is the central brain of the application; the place where the main() method is implemented
+ */
+struct nexus;
+
+#include <event2/event.h>
+#include "signals.h"
+#include "module.h"
+#include "irc_client.h"
+#include "nexus_lua.h"
+#include "lua_console.h"
+
+/**
+ * The central brain, as created in the main() function.
+ */
+struct nexus {
+ /** The libevent base */
+ struct event_base *ev_base;
+
+ /** Our signal handlers */
+ struct signals *signals;
+
+ /** Our loaded modules */
+ struct modules *modules;
+
+ /** The IRC client state */
+ struct irc_client *client;
+
+ /** Our lua state */
+ struct nexus_lua *lua;
+
+ /** Our lua console */
+ struct lua_console *lua_console;
+
+ /** Shutting down? */
+ bool shutdown;
+};
+
+/**
+ * Load a config file into the nexus from the given path
+ */
+err_t nexus_load_config (struct nexus *nexus, const char *path, error_t *err);
+
+/**
+ * Shut down the nexus cleanly. It /should/ be safe to call this several times, but it won't do anything.
+ *
+ * Once everything has shut down nicely (which it should, unless there's something buggy), the event loop should exit,
+ * and the main() function return.
+ */
+void nexus_shutdown (struct nexus *nexus);
+
+/**
+ * Shut down the nexus fast. This will stop the event loop right away, which should lead to nexus_destroy being called.
+ */
+void nexus_crash (struct nexus *nexus);
+
+/**
+ * Destroy the nexus directly. This is intended to be used once the event loop as exited.
+ */
+void nexus_destroy (struct nexus *nexus);
+
+/**
+ * The nexus main function, application entry point, etc.
+ */
+int main (int argc, char **argv);
+
+#endif /* NEXUS_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/spbot/signals.c Wed May 27 23:57:48 2009 +0300
@@ -0,0 +1,139 @@
+
+#include "signals.h"
+#include "log.h"
+
+#define _GNU_SOURCE
+
+#include <string.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <assert.h>
+
+struct signals {
+ /** The libevent base to use */
+ struct event_base *ev_base;
+
+ /** Information about up to MAX_SIGNALS signals */
+ struct signal {
+ /** The event we use to handle the signal */
+ struct event *ev;
+
+ } sig_list[MAX_SIGNALS];
+
+ /** Number of used sig_list slots */
+ int sig_count;
+};
+
+void signals_loopexit (int signal, short event, void *arg)
+{
+ struct signals *signals = arg;
+
+ (void) event;
+
+ log_info("caught %s: exiting", /* strsignal(signal) */ "xxx");
+
+ if (event_base_loopexit(signals->ev_base, NULL))
+ FATAL("event_base_loopexit");
+}
+
+void signals_ignore (int signal, short event, void *arg)
+{
+ struct signals *signals = arg;
+
+ (void) signal;
+ (void) event;
+ (void) arg;
+ (void) signals;
+
+ /* ignore */
+}
+
+err_t signals_create (struct signals **signals_ptr, struct event_base *ev_base)
+{
+ struct signals *signals;
+
+ if ((signals = calloc(1, sizeof(*signals))) == NULL)
+ return ERR_CALLOC;
+
+ // simple attributes
+ signals->ev_base = ev_base;
+
+ // done
+ *signals_ptr = signals;
+
+ return SUCCESS;
+}
+
+err_t signals_add (struct signals *signals, int sigval, void (*sig_handler)(evutil_socket_t, short, void *), void *arg)
+{
+ struct signal *sig_info;
+
+ // find our sig_info
+ assert(signals->sig_count < MAX_SIGNALS);
+ sig_info = signals->sig_list + (signals->sig_count++);
+
+ // set up the libevent signal events
+ if ((sig_info->ev = evsignal_new(signals->ev_base, sigval, sig_handler, arg)) == NULL)
+ return ERR_EVENT_NEW;
+
+ if (evsignal_add(sig_info->ev, NULL))
+ return ERR_EVENT_ADD;
+
+ // ok
+ return SUCCESS;
+}
+
+err_t signal_ignore (int signum, struct error_info *err)
+{
+ struct sigaction act = {
+ .sa_handler = SIG_IGN,
+ };
+
+ // signall the handler
+ if (sigaction(signum, &act, NULL))
+ RETURN_SET_ERROR_ERRNO(err, ERR_SIGACTION);
+
+ // ok
+ return SUCCESS;
+}
+
+struct signals *signals_default (struct event_base *ev_base)
+{
+ struct signals *signals = NULL;
+ struct error_info err;
+
+ // alloc signals
+ if (signals_create(&signals, ev_base))
+ return NULL;
+
+ // add the set of default signals
+ if (signal_ignore(SIGPIPE, &err))
+ log_error(&err, "signal_ignore(SIGPIPE)");
+
+ if ((ERROR_CODE(&err) = signals_add(signals, SIGINT, &signals_loopexit, signals))) {
+ log_error(&err, "signals_add(SIGINT)");
+ goto error;
+ }
+
+ // ok
+ return signals;
+
+error:
+ signals_free(signals);
+
+ return NULL;
+}
+
+void signals_free (struct signals *signals)
+{
+ int i;
+
+ // free all events
+ for (i = 0; i < signals->sig_count; i++) {
+ event_free(signals->sig_list[i].ev);
+ }
+
+ // free the info itself
+ free(signals);
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/spbot/signals.h Wed May 27 23:57:48 2009 +0300
@@ -0,0 +1,61 @@
+#ifndef SPBOT_SIGNALS_H
+#define SPBOT_SIGNALS_H
+
+/**
+ * @file
+ *
+ * Some kind of convenience stuff to handle signals, but actually pretty crap.
+ */
+#include <lib/error.h>
+#include <event2/event.h>
+
+/**
+ * How many signals we can define actions for
+ */
+#define MAX_SIGNALS 8
+
+/**
+ * State for a set of signals
+ */
+struct signals;
+
+/**
+ * Used as a handler for signals that should cause a loopexit.
+ */
+void signals_loopexit (int signal, short event, void *arg);
+
+/**
+ * Used to receive signals, but discard them, and continue what we were doing.
+ */
+void signals_ignore (int signal, short event, void *arg);
+
+/**
+ * Allocate a signals struct, acting on the given ev_base.
+ *
+ * Returns NULL on failure
+ */
+err_t signals_create (struct signals **signals_ptr, struct event_base *ev_base);
+
+/**
+ * Add a signal to be handled by the given signals struct with the given handler.
+ */
+err_t signals_add (struct signals *signals, int sigval, void (*sig_handler)(evutil_socket_t, short, void *), void *arg);
+
+/**
+ * Ignore the given sigval.
+ */
+err_t signal_ignore (int signum, error_t *err);
+
+/**
+ * Add a set of default signals
+ * SIGPIPE signals_ignore
+ * SIGINT signals_loopexit
+ */
+struct signals *signals_default (struct event_base *ev_base);
+
+/**
+ * Free the resources/handlers associated with the given signal handler set
+ */
+void signals_free (struct signals *signals);
+
+#endif /* SPBOT_SIGNALS_H */