move irc_log.c to modules/irc_log.c, and restructure sock_* to split the basic fd-level stuff out of sock_tcp and into sock_fd
--- a/src/CMakeLists.txt Thu Apr 02 03:19:44 2009 +0300
+++ b/src/CMakeLists.txt Tue Apr 07 18:09:16 2009 +0300
@@ -10,14 +10,14 @@
# define our source code modules
set (CORE_SOURCES error.c log.c)
-set (SOCK_SOURCES sock.c sock_tcp.c sock_gnutls.c sock_test.c line_proto.c)
+set (SOCK_SOURCES sock.c sock_fd.c sock_tcp.c sock_gnutls.c sock_test.c line_proto.c)
set (IRC_SOURCES irc_line.c irc_conn.c irc_net.c irc_chan.c chain.c irc_cmd.c irc_proto.c irc_client.c irc_user.c irc_queue.c)
set (LUA_SOURCES nexus_lua.c lua_objs.c lua_config.c lua_irc.c)
set (CONSOLE_SOURCES console.c lua_console.c)
set (NEXUS_SOURCES nexus.c ${CORE_SOURCES} ${SOCK_SOURCES} ${IRC_SOURCES} ${LUA_SOURCES} ${CONSOLE_SOURCES} signals.c module.c config.c)
set (TEST_SOURCES test.c ${CORE_SOURCES} ${SOCK_SOURCES} ${IRC_SOURCES})
-set (IRC_LOG_SOURCES irc_log.c)
+set (IRC_LOG_SOURCES modules/irc_log.c)
# define our libraries
set (MODULE_LIBRARIES "dl")
--- a/src/irc_log.c Thu Apr 02 03:19:44 2009 +0300
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,529 +0,0 @@
-#include "module.h"
-#include "irc_chan.h"
-#include "config.h"
-#include "error.h"
-#include "log.h"
-
-#include <stdlib.h>
-#include <string.h>
-
-#include <event2/event.h>
-#include <evsql.h>
-
-/**
- * The irc_log module state.
- */
-struct irc_log_ctx {
- /** The nexus this module is loaded for */
- struct nexus *nexus;
-
- /** The database connection */
- struct evsql *db;
-
- /**
- * Our logged channels
- */
- TAILQ_HEAD(irc_log_ctx_channels, irc_log_chan) channels;
-
- /** Are we currently unloading ourself (via irc_log_unload) ? */
- bool unloading;
-
- /** The `struct module` passed to us once we unload() */
- struct module *module;
-};
-
-/**
- * State required to use irc_cmd_handler with irc_chan.handlers.
- */
-struct irc_log_chan {
- /** The irc_log context */
- struct irc_log_ctx *ctx;
-
- /** The target channel */
- struct irc_chan *chan;
-
- /** Are we stopping (irc_log_chan_stop called)? */
- bool stopping;
-
- /** We are part of the irc_log_ctx.channels list */
- TAILQ_ENTRY(irc_log_chan) ctx_channels;
-};
-
-/*
- * Forward-declare
- */
-static void irc_log_chan_destroy (struct irc_log_chan *chan_ctx);
-
-/**
- * The irc_log_ctx has completed stopping all the channels, and should now destroy itself
- */
-static void irc_log_stopped (struct irc_log_ctx *ctx)
-{
- log_debug("module unload() completed");
-
- // notify
- module_unloaded(ctx->module);
-}
-
-/**
- * The irc_log_chan has completed shutdown
- */
-static void irc_log_chan_stopped (struct irc_log_chan *chan_ctx)
-{
- struct irc_log_ctx *ctx = chan_ctx->ctx;
-
- log_debug("destroying the irc_log_chan (chan=%s)", irc_chan_name(chan_ctx->chan));
-
- // destroy the channel
- irc_log_chan_destroy(chan_ctx);
-
- // was it the final channel for unloading?
- if (ctx->unloading && TAILQ_EMPTY(&ctx->channels))
- irc_log_stopped(ctx);
-}
-
-/**
- * Our evsql result handler for irc_log_event INSERTs.
- */
-static void irc_log_on_sql_result (struct evsql_result *res, void *arg)
-{
- struct irc_log_chan *chan_ctx = arg;
- err_t err;
-
- // check errors
- if ((err = evsql_result_check(res)))
- log_error("irc_log_event: %s", evsql_result_error(res));
-
- // ok, don't need the result anymore
- evsql_result_free(res);
-
- // if stopping, then handle that
- if (chan_ctx->stopping)
- irc_log_chan_stopped(chan_ctx);
-}
-
-/**
- * Log the event into our database.
- *
- * Any of chan_ctx, source, target, message can be NULL to insert NULLs
- */
-static err_t irc_log_event (struct irc_log_ctx *ctx, struct irc_log_chan *chan_ctx, const struct irc_nm *source,
- const char *type, const char *target, const char *message)
-{
- struct evsql_query *query;
-
- // unpack the nick/user/hostname args, as source can be NULL
- const char *network = chan_ctx ? chan_ctx->chan->net->info.network : NULL;
- const char *channel = chan_ctx ? irc_chan_name(chan_ctx->chan) : NULL;
- const char *nickname = source ? source->nickname : NULL;
- const char *username = source ? source->username : NULL;
- const char *hostname = source ? source->hostname : NULL;
-
- // our SQL query
- static struct evsql_query_info sql = {
- "INSERT INTO logs ("
- " network, channel, nickname, username, hostname, type, target, message"
- " ) VALUES ("
- " $1::varchar, $2::varchar, $3::varchar, $4::varchar, $5::varchar, $6::varchar, $7::varchar, $8::varchar"
- " )", {
- EVSQL_TYPE(STRING), // network
- EVSQL_TYPE(STRING), // channel
- EVSQL_TYPE(STRING), // nickname
- EVSQL_TYPE(STRING), // username
- EVSQL_TYPE(STRING), // hostname
- EVSQL_TYPE(STRING), // type
- EVSQL_TYPE(STRING), // target
- EVSQL_TYPE(STRING), // message
- EVSQL_TYPE_END
- }
- };
-
- // drop lines if not connected
- if (ctx->db == NULL) {
- log_warn("no database connected");
-
- return ERR_MODULE_CONF;
- }
-
- // run the INSERT
- if ((query = evsql_query_exec(ctx->db, NULL, &sql, &irc_log_on_sql_result, chan_ctx,
- network, channel, nickname, username, hostname, type, target, message
- )) == NULL) {
- // XXX: get evsql_query error info somehow...
- log_error("evsql_query_exec failed for %s:%s - %s!%s@%s - %s -> %s - %s",
- network, channel, nickname, username, hostname, type, target, message
- );
-
- return ERR_EVSQL_QUERY_EXEC;
- }
-
- // ok
- log_debug("%s:%s - %s!%s@%s - %s -> %s - %s",
- network, channel, nickname, username, hostname, type, target, message
- );
-
- return SUCCESS;
-}
-
-/**
- * Log a simple channel event of the form:
- *
- * :nm <type> <channel> [<msg>]
- */
-static void irc_log_on_chan_generic (const struct irc_line *line, void *arg)
-{
- struct irc_log_chan *chan_ctx = arg;
-
- const char *msg = line->args[1];
-
- irc_log_event(chan_ctx->ctx, chan_ctx, line->source, line->command, NULL, msg);
-}
-
-/**
- * Log a NICK event on a channel
- *
- * :nm NICK <nickname>
- *
- * This logs the new nickname as the target
- */
-static void irc_log_on_chan_NICK (const struct irc_line *line, void *arg)
-{
- struct irc_log_chan *chan_ctx = arg;
-
- const char *nickname = line->args[0];
-
- // log it
- irc_log_event(chan_ctx->ctx, chan_ctx, line->source, line->command, nickname, NULL);
-}
-
-/**
- * Log a QUIT event on a channel
- *
- * :nm QUIT [<message>]
- */
-static void irc_log_on_chan_QUIT (const struct irc_line *line, void *arg)
-{
- struct irc_log_chan *chan_ctx = arg;
-
- const char *message = line->args[0];
-
- // log it
- irc_log_event(chan_ctx->ctx, chan_ctx, line->source, line->command, NULL, message);
-}
-
-/**
- * Log a MODE event on a channel
- *
- * :nm MODE <channel> [<modearg> [...]]
- *
- * This conacts all the modeargs together, and logs that as the message
- */
-static void irc_log_on_chan_MODE (const struct irc_line *line, void *arg)
-{
- struct irc_log_chan *chan_ctx = arg;
- char message[512], *ptr = message;
- const char *cmdarg;
-
- // iterate over each arg
- FOREACH_IRC_LINE_ARGS(line, cmdarg) {
- // separate with spaces
- if (cmdarg > line->args[0])
- *ptr++ = ' ';
-
- // append
- ptr += snprintf(ptr, (message + 512 - ptr), "%s", cmdarg);
-
- // buffer full?
- if (ptr > message + 512)
- break;
- }
-
- // log
- irc_log_event(chan_ctx->ctx, chan_ctx, line->source, line->command, NULL, message);
-}
-
-/**
- * Log a KICK event on a channel
- *
- * :nm KICK <channel> <target> [<comment>]
- */
-static void irc_log_on_chan_KICK (const struct irc_line *line, void *arg)
-{
- struct irc_log_chan *chan_ctx = arg;
-
- const char *target = line->args[1];
- const char *msg = line->args[2];
-
- irc_log_event(chan_ctx->ctx, chan_ctx, line->source, line->command, target, msg);
-}
-
-/**
- * Log a CTCP ACTION message on a channel
- *
- * :nm "CTCP ACTION" <channel> <action>
- */
-static void irc_log_on_chan_CTCP_ACTION (const struct irc_line *line, void *arg)
-{
- struct irc_log_chan *chan_ctx = arg;
-
- const char *action = line->args[1];
-
- irc_log_event(chan_ctx->ctx, chan_ctx, line->source, "ACTION", NULL, action);
-}
-
-/**
- * Our low-level channel-specific message handlers for logged channels.
- *
- * Note that these get a `struct irc_log_chan*` as an argument.
- */
-static struct irc_cmd_handler _chan_cmd_handlers[] = {
- { "NICK", &irc_log_on_chan_NICK },
- { "QUIT", &irc_log_on_chan_QUIT },
- { "JOIN", &irc_log_on_chan_generic },
- { "PART", &irc_log_on_chan_generic },
- { "MODE", &irc_log_on_chan_MODE },
- { "TOPIC", &irc_log_on_chan_generic },
- { "KICK", &irc_log_on_chan_KICK },
- { "PRIVMSG", &irc_log_on_chan_generic },
- { "NOTICE", &irc_log_on_chan_generic },
- { "CTCP ACTION", &irc_log_on_chan_CTCP_ACTION },
- { NULL, NULL }
-};
-
-/**
- * Our high-level channel-specific callbacks for logged channels.
- *
- * Note that these get a `struct irc_log_chan*` as an argument.
- */
-static struct irc_chan_callbacks _chan_callbacks = {
- .on_self_join = NULL,
- .on_msg = NULL,
-};
-
-/**
- * Release resources associated with the given irc_log_chan without doing any clean shutdown stuff
- */
-static void irc_log_chan_destroy (struct irc_log_chan *chan_ctx)
-{
- // remove any handlers/callbacks
- irc_cmd_remove(&chan_ctx->chan->handlers, _chan_cmd_handlers, chan_ctx);
- irc_chan_remove_callbacks(chan_ctx->chan, &_chan_callbacks, chan_ctx);
-
- // remove from channels list
- TAILQ_REMOVE(&chan_ctx->ctx->channels, chan_ctx, ctx_channels);
-
- // free ourselves
- free(chan_ctx);
-}
-
-/**
- * Begin logging the given channel
- */
-static err_t irc_log_chan (struct irc_log_ctx *ctx, struct irc_chan *chan, struct error_info *err)
-{
- struct irc_log_chan *chan_ctx;
-
- // alloc
- if ((chan_ctx = calloc(1, sizeof(*chan_ctx))) == NULL)
- return SET_ERROR(err, ERR_CALLOC);
-
- // store
- chan_ctx->ctx = ctx;
- chan_ctx->chan = chan;
-
- // add to channels list
- TAILQ_INSERT_TAIL(&ctx->channels, chan_ctx, ctx_channels);
-
- // add low-level handlers
- if ((ERROR_CODE(err) = irc_cmd_add(&chan_ctx->chan->handlers, _chan_cmd_handlers, chan_ctx)))
- goto error;
-
- // add channel callbacks
- if ((ERROR_CODE(err) = irc_chan_add_callbacks(chan_ctx->chan, &_chan_callbacks, chan_ctx)))
- goto error;
-
- // log an OPEN message
- // XXX: move this to when we first JOIN the channel
- if ((ERROR_CODE(err) = irc_log_event(ctx, chan_ctx, NULL, "OPEN", NULL, NULL)))
- goto error;
-
- // ok
- log_info("logging channel %s:%s", chan_ctx->chan->net->info.network, irc_chan_name(chan_ctx->chan));
-
- return SUCCESS;
-
-error:
- // cleanup
- irc_log_chan_destroy(chan_ctx);
-
- return ERROR_CODE(err);
-}
-
-/**
- * Stop logging the given channel, shutting it down nicely and then releasing its resources.
- *
- * If shutting it down nicely fails, this just destroys it.
- */
-static err_t irc_log_chan_stop (struct irc_log_chan *chan_ctx)
-{
- err_t err;
-
- // mark it as stopping, so the result callback knows to destroy it
- chan_ctx->stopping = true;
-
- // log the CLOSE event
- if ((err = irc_log_event(chan_ctx->ctx, chan_ctx, NULL, "CLOSE", NULL, NULL)))
- goto error;
-
- // ok
- return SUCCESS;
-
-error:
- // destroy it uncleanly
- irc_log_chan_destroy(chan_ctx);
-
- return err;
-}
-
-/**
- * Allocate and initialize an irc_log_ctx. This doesn't do very much, the real magic happens in irc_log_conf.
- */
-static err_t irc_log_init (struct nexus *nexus, void **ctx_ptr, struct error_info *err)
-{
- struct irc_log_ctx *ctx;
-
- // allocate
- if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
- return SET_ERROR(err, ERR_CALLOC);
-
- // store
- ctx->nexus = nexus;
-
- // initialize
- TAILQ_INIT(&ctx->channels);
-
- log_info("module initialized");
-
- // ok
- *ctx_ptr = ctx;
-
- return SUCCESS;
-}
-
-/**
- * Process the irc_log.db_info config option.
- *
- * Creates a new evsql handle.
- *
- * Fails if ctx->db is already set.
- */
-static err_t irc_log_conf_db_info (void *_ctx, char *value, struct error_info *err)
-{
- struct irc_log_ctx *ctx = _ctx;
-
- log_info("connect to database: %s", value);
-
- // already connected?
- if (ctx->db)
- RETURN_SET_ERROR_STR(err, ERR_MODULE_CONF, "irc_log.db_info already set");
-
- // create a new evsql handle
- if ((ctx->db = evsql_new_pq(ctx->nexus->ev_base, value, NULL, NULL)) == NULL)
- return SET_ERROR(err, ERR_EVSQL_NEW_PQ);
-
- // ok
- return SUCCESS;
-}
-
-/**
- * Process the irc_log.channel config option.
- *
- * Creates a new irc_log_chan context, looks up the channel, adds our command handlers, and logs an OPEN event.
- *
- * Fails if the value is invalid, we don't have a database connected, the channel doesn't exist, adding our command
- * handlers/callbacks fails, or sending the initial INSERT-OPEN query fails.
- */
-static err_t irc_log_conf_channel (void *_ctx, struct irc_chan *chan, struct error_info *err)
-{
- struct irc_log_ctx *ctx = _ctx;
-
- // have a db configured?
- if (!ctx->db)
- RETURN_SET_ERROR_STR(err, ERR_MODULE_CONF, "irc_log.channel used without any irc_log.db_info");
-
- // begin logging it
- if (irc_log_chan(ctx, chan, err))
- return ERROR_CODE(err);
-
- // ok
- return SUCCESS;
-}
-
-/**
- * Our configuration options
- */
-struct config_option irc_log_config_options[] = {
- CONFIG_OPT_STRING( "db_info", &irc_log_conf_db_info, "[<key>=<value> [...]]", "set database connection info, see libpq docs" ),
- CONFIG_OPT_IRC_CHAN( "channel", &irc_log_conf_channel, "<channel>", "log the given channel" ),
-
- CONFIG_OPT_END
-};
-
-/**
- * Deinitialize, logging CLOSE events for all channels, and removing any hooks we've added
- */
-static err_t irc_log_unload (void *_ctx, struct module *module)
-{
- struct irc_log_ctx *ctx = _ctx;
- struct irc_log_chan *chan_ctx;
-
- // update our state to mark ourselves as unloading
- ctx->unloading = true;
- ctx->module = module;
-
- if (TAILQ_EMPTY(&ctx->channels)) {
- // nothing to do, unloaded now
- module_unloaded(module);
-
- } else {
- // stop logging each channel
- TAILQ_FOREACH(chan_ctx, &ctx->channels, ctx_channels) {
- irc_log_chan_stop(chan_ctx);
- }
-
- // once they have all stopped, we will be unloaded
- }
-
- // wait for all the channels to be stopped, which will call irc_log_stopped
- return SUCCESS;
-}
-
-/**
- * We can safely destroy the evsql instance from here, since we're outside of any callbacks from this module.
- *
- * Note that we might not have had any of the config options called...
- */
-static void irc_log_destroy (void *_ctx)
-{
- struct irc_log_ctx *ctx = _ctx;
-
- log_debug("destroying the irc_log_ctx");
-
- if (ctx->db)
- // destroy the evsql instance
- evsql_destroy(ctx->db);
-
- // ...no more
- free(ctx);
-}
-
-/**
- * The module function table
- */
-struct module_desc irc_log_module = {
- .init = &irc_log_init,
- .config_options = irc_log_config_options,
- .unload = &irc_log_unload,
- .destroy = &irc_log_destroy
-};
-
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/modules/irc_log.c Tue Apr 07 18:09:16 2009 +0300
@@ -0,0 +1,529 @@
+#include "../module.h"
+#include "../irc_chan.h"
+#include "../config.h"
+#include "../error.h"
+#include "../log.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+#include <event2/event.h>
+#include <evsql.h>
+
+/**
+ * The irc_log module state.
+ */
+struct irc_log_ctx {
+ /** The nexus this module is loaded for */
+ struct nexus *nexus;
+
+ /** The database connection */
+ struct evsql *db;
+
+ /**
+ * Our logged channels
+ */
+ TAILQ_HEAD(irc_log_ctx_channels, irc_log_chan) channels;
+
+ /** Are we currently unloading ourself (via irc_log_unload) ? */
+ bool unloading;
+
+ /** The `struct module` passed to us once we unload() */
+ struct module *module;
+};
+
+/**
+ * State required to use irc_cmd_handler with irc_chan.handlers.
+ */
+struct irc_log_chan {
+ /** The irc_log context */
+ struct irc_log_ctx *ctx;
+
+ /** The target channel */
+ struct irc_chan *chan;
+
+ /** Are we stopping (irc_log_chan_stop called)? */
+ bool stopping;
+
+ /** We are part of the irc_log_ctx.channels list */
+ TAILQ_ENTRY(irc_log_chan) ctx_channels;
+};
+
+/*
+ * Forward-declare
+ */
+static void irc_log_chan_destroy (struct irc_log_chan *chan_ctx);
+
+/**
+ * The irc_log_ctx has completed stopping all the channels, and should now destroy itself
+ */
+static void irc_log_stopped (struct irc_log_ctx *ctx)
+{
+ log_debug("module unload() completed");
+
+ // notify
+ module_unloaded(ctx->module);
+}
+
+/**
+ * The irc_log_chan has completed shutdown
+ */
+static void irc_log_chan_stopped (struct irc_log_chan *chan_ctx)
+{
+ struct irc_log_ctx *ctx = chan_ctx->ctx;
+
+ log_debug("destroying the irc_log_chan (chan=%s)", irc_chan_name(chan_ctx->chan));
+
+ // destroy the channel
+ irc_log_chan_destroy(chan_ctx);
+
+ // was it the final channel for unloading?
+ if (ctx->unloading && TAILQ_EMPTY(&ctx->channels))
+ irc_log_stopped(ctx);
+}
+
+/**
+ * Our evsql result handler for irc_log_event INSERTs.
+ */
+static void irc_log_on_sql_result (struct evsql_result *res, void *arg)
+{
+ struct irc_log_chan *chan_ctx = arg;
+ err_t err;
+
+ // check errors
+ if ((err = evsql_result_check(res)))
+ log_error("irc_log_event: %s", evsql_result_error(res));
+
+ // ok, don't need the result anymore
+ evsql_result_free(res);
+
+ // if stopping, then handle that
+ if (chan_ctx->stopping)
+ irc_log_chan_stopped(chan_ctx);
+}
+
+/**
+ * Log the event into our database.
+ *
+ * Any of chan_ctx, source, target, message can be NULL to insert NULLs
+ */
+static err_t irc_log_event (struct irc_log_ctx *ctx, struct irc_log_chan *chan_ctx, const struct irc_nm *source,
+ const char *type, const char *target, const char *message)
+{
+ struct evsql_query *query;
+
+ // unpack the nick/user/hostname args, as source can be NULL
+ const char *network = chan_ctx ? chan_ctx->chan->net->info.network : NULL;
+ const char *channel = chan_ctx ? irc_chan_name(chan_ctx->chan) : NULL;
+ const char *nickname = source ? source->nickname : NULL;
+ const char *username = source ? source->username : NULL;
+ const char *hostname = source ? source->hostname : NULL;
+
+ // our SQL query
+ static struct evsql_query_info sql = {
+ "INSERT INTO logs ("
+ " network, channel, nickname, username, hostname, type, target, message"
+ " ) VALUES ("
+ " $1::varchar, $2::varchar, $3::varchar, $4::varchar, $5::varchar, $6::varchar, $7::varchar, $8::varchar"
+ " )", {
+ EVSQL_TYPE(STRING), // network
+ EVSQL_TYPE(STRING), // channel
+ EVSQL_TYPE(STRING), // nickname
+ EVSQL_TYPE(STRING), // username
+ EVSQL_TYPE(STRING), // hostname
+ EVSQL_TYPE(STRING), // type
+ EVSQL_TYPE(STRING), // target
+ EVSQL_TYPE(STRING), // message
+ EVSQL_TYPE_END
+ }
+ };
+
+ // drop lines if not connected
+ if (ctx->db == NULL) {
+ log_warn("no database connected");
+
+ return ERR_MODULE_CONF;
+ }
+
+ // run the INSERT
+ if ((query = evsql_query_exec(ctx->db, NULL, &sql, &irc_log_on_sql_result, chan_ctx,
+ network, channel, nickname, username, hostname, type, target, message
+ )) == NULL) {
+ // XXX: get evsql_query error info somehow...
+ log_error("evsql_query_exec failed for %s:%s - %s!%s@%s - %s -> %s - %s",
+ network, channel, nickname, username, hostname, type, target, message
+ );
+
+ return ERR_EVSQL_QUERY_EXEC;
+ }
+
+ // ok
+ log_debug("%s:%s - %s!%s@%s - %s -> %s - %s",
+ network, channel, nickname, username, hostname, type, target, message
+ );
+
+ return SUCCESS;
+}
+
+/**
+ * Log a simple channel event of the form:
+ *
+ * :nm <type> <channel> [<msg>]
+ */
+static void irc_log_on_chan_generic (const struct irc_line *line, void *arg)
+{
+ struct irc_log_chan *chan_ctx = arg;
+
+ const char *msg = line->args[1];
+
+ irc_log_event(chan_ctx->ctx, chan_ctx, line->source, line->command, NULL, msg);
+}
+
+/**
+ * Log a NICK event on a channel
+ *
+ * :nm NICK <nickname>
+ *
+ * This logs the new nickname as the target
+ */
+static void irc_log_on_chan_NICK (const struct irc_line *line, void *arg)
+{
+ struct irc_log_chan *chan_ctx = arg;
+
+ const char *nickname = line->args[0];
+
+ // log it
+ irc_log_event(chan_ctx->ctx, chan_ctx, line->source, line->command, nickname, NULL);
+}
+
+/**
+ * Log a QUIT event on a channel
+ *
+ * :nm QUIT [<message>]
+ */
+static void irc_log_on_chan_QUIT (const struct irc_line *line, void *arg)
+{
+ struct irc_log_chan *chan_ctx = arg;
+
+ const char *message = line->args[0];
+
+ // log it
+ irc_log_event(chan_ctx->ctx, chan_ctx, line->source, line->command, NULL, message);
+}
+
+/**
+ * Log a MODE event on a channel
+ *
+ * :nm MODE <channel> [<modearg> [...]]
+ *
+ * This conacts all the modeargs together, and logs that as the message
+ */
+static void irc_log_on_chan_MODE (const struct irc_line *line, void *arg)
+{
+ struct irc_log_chan *chan_ctx = arg;
+ char message[512], *ptr = message;
+ const char *cmdarg;
+
+ // iterate over each arg
+ FOREACH_IRC_LINE_ARGS(line, cmdarg) {
+ // separate with spaces
+ if (cmdarg > line->args[0])
+ *ptr++ = ' ';
+
+ // append
+ ptr += snprintf(ptr, (message + 512 - ptr), "%s", cmdarg);
+
+ // buffer full?
+ if (ptr > message + 512)
+ break;
+ }
+
+ // log
+ irc_log_event(chan_ctx->ctx, chan_ctx, line->source, line->command, NULL, message);
+}
+
+/**
+ * Log a KICK event on a channel
+ *
+ * :nm KICK <channel> <target> [<comment>]
+ */
+static void irc_log_on_chan_KICK (const struct irc_line *line, void *arg)
+{
+ struct irc_log_chan *chan_ctx = arg;
+
+ const char *target = line->args[1];
+ const char *msg = line->args[2];
+
+ irc_log_event(chan_ctx->ctx, chan_ctx, line->source, line->command, target, msg);
+}
+
+/**
+ * Log a CTCP ACTION message on a channel
+ *
+ * :nm "CTCP ACTION" <channel> <action>
+ */
+static void irc_log_on_chan_CTCP_ACTION (const struct irc_line *line, void *arg)
+{
+ struct irc_log_chan *chan_ctx = arg;
+
+ const char *action = line->args[1];
+
+ irc_log_event(chan_ctx->ctx, chan_ctx, line->source, "ACTION", NULL, action);
+}
+
+/**
+ * Our low-level channel-specific message handlers for logged channels.
+ *
+ * Note that these get a `struct irc_log_chan*` as an argument.
+ */
+static struct irc_cmd_handler _chan_cmd_handlers[] = {
+ { "NICK", &irc_log_on_chan_NICK },
+ { "QUIT", &irc_log_on_chan_QUIT },
+ { "JOIN", &irc_log_on_chan_generic },
+ { "PART", &irc_log_on_chan_generic },
+ { "MODE", &irc_log_on_chan_MODE },
+ { "TOPIC", &irc_log_on_chan_generic },
+ { "KICK", &irc_log_on_chan_KICK },
+ { "PRIVMSG", &irc_log_on_chan_generic },
+ { "NOTICE", &irc_log_on_chan_generic },
+ { "CTCP ACTION", &irc_log_on_chan_CTCP_ACTION },
+ { NULL, NULL }
+};
+
+/**
+ * Our high-level channel-specific callbacks for logged channels.
+ *
+ * Note that these get a `struct irc_log_chan*` as an argument.
+ */
+static struct irc_chan_callbacks _chan_callbacks = {
+ .on_self_join = NULL,
+ .on_msg = NULL,
+};
+
+/**
+ * Release resources associated with the given irc_log_chan without doing any clean shutdown stuff
+ */
+static void irc_log_chan_destroy (struct irc_log_chan *chan_ctx)
+{
+ // remove any handlers/callbacks
+ irc_cmd_remove(&chan_ctx->chan->handlers, _chan_cmd_handlers, chan_ctx);
+ irc_chan_remove_callbacks(chan_ctx->chan, &_chan_callbacks, chan_ctx);
+
+ // remove from channels list
+ TAILQ_REMOVE(&chan_ctx->ctx->channels, chan_ctx, ctx_channels);
+
+ // free ourselves
+ free(chan_ctx);
+}
+
+/**
+ * Begin logging the given channel
+ */
+static err_t irc_log_chan (struct irc_log_ctx *ctx, struct irc_chan *chan, struct error_info *err)
+{
+ struct irc_log_chan *chan_ctx;
+
+ // alloc
+ if ((chan_ctx = calloc(1, sizeof(*chan_ctx))) == NULL)
+ return SET_ERROR(err, ERR_CALLOC);
+
+ // store
+ chan_ctx->ctx = ctx;
+ chan_ctx->chan = chan;
+
+ // add to channels list
+ TAILQ_INSERT_TAIL(&ctx->channels, chan_ctx, ctx_channels);
+
+ // add low-level handlers
+ if ((ERROR_CODE(err) = irc_cmd_add(&chan_ctx->chan->handlers, _chan_cmd_handlers, chan_ctx)))
+ goto error;
+
+ // add channel callbacks
+ if ((ERROR_CODE(err) = irc_chan_add_callbacks(chan_ctx->chan, &_chan_callbacks, chan_ctx)))
+ goto error;
+
+ // log an OPEN message
+ // XXX: move this to when we first JOIN the channel
+ if ((ERROR_CODE(err) = irc_log_event(ctx, chan_ctx, NULL, "OPEN", NULL, NULL)))
+ goto error;
+
+ // ok
+ log_info("logging channel %s:%s", chan_ctx->chan->net->info.network, irc_chan_name(chan_ctx->chan));
+
+ return SUCCESS;
+
+error:
+ // cleanup
+ irc_log_chan_destroy(chan_ctx);
+
+ return ERROR_CODE(err);
+}
+
+/**
+ * Stop logging the given channel, shutting it down nicely and then releasing its resources.
+ *
+ * If shutting it down nicely fails, this just destroys it.
+ */
+static err_t irc_log_chan_stop (struct irc_log_chan *chan_ctx)
+{
+ err_t err;
+
+ // mark it as stopping, so the result callback knows to destroy it
+ chan_ctx->stopping = true;
+
+ // log the CLOSE event
+ if ((err = irc_log_event(chan_ctx->ctx, chan_ctx, NULL, "CLOSE", NULL, NULL)))
+ goto error;
+
+ // ok
+ return SUCCESS;
+
+error:
+ // destroy it uncleanly
+ irc_log_chan_destroy(chan_ctx);
+
+ return err;
+}
+
+/**
+ * Allocate and initialize an irc_log_ctx. This doesn't do very much, the real magic happens in irc_log_conf.
+ */
+static err_t irc_log_init (struct nexus *nexus, void **ctx_ptr, struct error_info *err)
+{
+ struct irc_log_ctx *ctx;
+
+ // allocate
+ if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
+ return SET_ERROR(err, ERR_CALLOC);
+
+ // store
+ ctx->nexus = nexus;
+
+ // initialize
+ TAILQ_INIT(&ctx->channels);
+
+ log_info("module initialized");
+
+ // ok
+ *ctx_ptr = ctx;
+
+ return SUCCESS;
+}
+
+/**
+ * Process the irc_log.db_info config option.
+ *
+ * Creates a new evsql handle.
+ *
+ * Fails if ctx->db is already set.
+ */
+static err_t irc_log_conf_db_info (void *_ctx, char *value, struct error_info *err)
+{
+ struct irc_log_ctx *ctx = _ctx;
+
+ log_info("connect to database: %s", value);
+
+ // already connected?
+ if (ctx->db)
+ RETURN_SET_ERROR_STR(err, ERR_MODULE_CONF, "irc_log.db_info already set");
+
+ // create a new evsql handle
+ if ((ctx->db = evsql_new_pq(ctx->nexus->ev_base, value, NULL, NULL)) == NULL)
+ return SET_ERROR(err, ERR_EVSQL_NEW_PQ);
+
+ // ok
+ return SUCCESS;
+}
+
+/**
+ * Process the irc_log.channel config option.
+ *
+ * Creates a new irc_log_chan context, looks up the channel, adds our command handlers, and logs an OPEN event.
+ *
+ * Fails if the value is invalid, we don't have a database connected, the channel doesn't exist, adding our command
+ * handlers/callbacks fails, or sending the initial INSERT-OPEN query fails.
+ */
+static err_t irc_log_conf_channel (void *_ctx, struct irc_chan *chan, struct error_info *err)
+{
+ struct irc_log_ctx *ctx = _ctx;
+
+ // have a db configured?
+ if (!ctx->db)
+ RETURN_SET_ERROR_STR(err, ERR_MODULE_CONF, "irc_log.channel used without any irc_log.db_info");
+
+ // begin logging it
+ if (irc_log_chan(ctx, chan, err))
+ return ERROR_CODE(err);
+
+ // ok
+ return SUCCESS;
+}
+
+/**
+ * Our configuration options
+ */
+struct config_option irc_log_config_options[] = {
+ CONFIG_OPT_STRING( "db_info", &irc_log_conf_db_info, "[<key>=<value> [...]]", "set database connection info, see libpq docs" ),
+ CONFIG_OPT_IRC_CHAN( "channel", &irc_log_conf_channel, "<channel>", "log the given channel" ),
+
+ CONFIG_OPT_END
+};
+
+/**
+ * Deinitialize, logging CLOSE events for all channels, and removing any hooks we've added
+ */
+static err_t irc_log_unload (void *_ctx, struct module *module)
+{
+ struct irc_log_ctx *ctx = _ctx;
+ struct irc_log_chan *chan_ctx;
+
+ // update our state to mark ourselves as unloading
+ ctx->unloading = true;
+ ctx->module = module;
+
+ if (TAILQ_EMPTY(&ctx->channels)) {
+ // nothing to do, unloaded now
+ module_unloaded(module);
+
+ } else {
+ // stop logging each channel
+ TAILQ_FOREACH(chan_ctx, &ctx->channels, ctx_channels) {
+ irc_log_chan_stop(chan_ctx);
+ }
+
+ // once they have all stopped, we will be unloaded
+ }
+
+ // wait for all the channels to be stopped, which will call irc_log_stopped
+ return SUCCESS;
+}
+
+/**
+ * We can safely destroy the evsql instance from here, since we're outside of any callbacks from this module.
+ *
+ * Note that we might not have had any of the config options called...
+ */
+static void irc_log_destroy (void *_ctx)
+{
+ struct irc_log_ctx *ctx = _ctx;
+
+ log_debug("destroying the irc_log_ctx");
+
+ if (ctx->db)
+ // destroy the evsql instance
+ evsql_destroy(ctx->db);
+
+ // ...no more
+ free(ctx);
+}
+
+/**
+ * The module function table
+ */
+struct module_desc irc_log_module = {
+ .init = &irc_log_init,
+ .config_options = irc_log_config_options,
+ .unload = &irc_log_unload,
+ .destroy = &irc_log_destroy
+};
+
--- a/src/sock.h Thu Apr 02 03:19:44 2009 +0300
+++ b/src/sock.h Tue Apr 07 18:09:16 2009 +0300
@@ -5,6 +5,8 @@
* @file
*
* Low-level socket-related functions
+ *
+ * XXX: not just sockets anymore
*/
#include "error.h"
#include <sys/types.h>
@@ -37,23 +39,16 @@
*/
enum sock_error_code {
_ERR_SOCK_BEGIN = _ERR_SOCK,
- ERR_SOCKET,
-
- /** connect() error, either direct or async */
- ERR_CONNECT,
- ERR_READ,
-
- /** EOF on read() */
- ERR_READ_EOF,
- ERR_WRITE,
-
- /** EOF on write() */
- ERR_WRITE_EOF,
- ERR_FCNTL,
-
- /** Lingering error on close() */
- ERR_CLOSE,
- ERR_GETSOCKOPT,
+ ERR_SOCKET, ///< socket(2) failed
+ ERR_CONNECT, ///< connect(2) error - either direct or async
+ ERR_READ, ///< read(2) error - will probably show up as an ERR_WRITE as well
+ ERR_READ_EOF, ///< EOF on read(2)
+ ERR_WRITE, ///< write(2) error - data was unsent, will probably show up as an ERR_READ as well
+ ERR_WRITE_EOF, ///< write(2) gave EOF - zero bytes written
+ ERR_FCNTL, ///< fcntl(2) failed
+ ERR_CLOSE, ///< close(2) failed, some written data was probably not sent
+ ERR_GETSOCKOPT, ///< getsockopt(2) failed
+ ERR_OPEN, ///< open(2) failed
};
/**
@@ -110,6 +105,11 @@
err_t sock_ssl_connect (struct sock_stream **sock_ptr, const char *host, const char *service, struct error_info *err);
/**
+ * A read-only "socket" based on a FIFO, this provides nonblocking read operations by re-opening the FIFO on EOF.
+ */
+err_t fifo_read_open (struct sock_stream **stream_ptr, const char *path, struct error_info *err);
+
+/**
* Read a series of bytes from the socket into the given \a buf (up to \a len bytes). If succesfull, this returns
* the number of bytes read (which will be less than or equal to \a len). If the socket is nonblocking (i.e.
* sock_stream_event_init() was set), and there is no data available, this returns zero, and one should use
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/sock_fd.c Tue Apr 07 18:09:16 2009 +0300
@@ -0,0 +1,185 @@
+#include "sock_fd.h"
+
+#include <fcntl.h>
+#include <unistd.h>
+#include <assert.h>
+
+void sock_fd_event_handler (evutil_socket_t fd, short what, void *arg)
+{
+ struct sock_fd *sock = arg;
+
+ (void) fd;
+
+ // invoke appropriate callback
+ sock_stream_invoke_callbacks(SOCK_FD_BASE(sock), what);
+}
+
+err_t sock_fd_read (struct sock_stream *base_sock, void *buf, size_t *len)
+{
+ struct sock_fd *sock = SOCK_FROM_BASE(base_sock, struct sock_fd);
+ int ret;
+
+ // read(), and detect non-EAGAIN or EOF
+ if ((ret = read(sock->fd, buf, *len)) < 0 && errno != EAGAIN)
+ // unexpected error
+ RETURN_SET_ERROR_ERRNO(SOCK_FD_ERR(sock), ERR_READ);
+
+ else if (ret == 0)
+ // EOF
+ return SET_ERROR(SOCK_FD_ERR(sock), ERR_READ_EOF);
+
+
+ if (ret < 0) {
+ // EAGAIN -> zero bytes
+ *len = 0;
+
+ } else {
+ // normal -> bytes read
+ *len = ret;
+ }
+
+ // ok
+ return SUCCESS;
+}
+
+err_t sock_fd_write (struct sock_stream *base_sock, const void *buf, size_t *len)
+{
+ struct sock_fd *sock = SOCK_FROM_BASE(base_sock, struct sock_fd);
+ int ret;
+
+ // write(), and detect non-EAGAIN or EOF
+ if ((ret = write(sock->fd, buf, *len)) < 0 && errno != EAGAIN)
+ // unexpected error
+ RETURN_SET_ERROR_ERRNO(SOCK_FD_ERR(sock), ERR_WRITE);
+
+ else if (ret == 0)
+ // EOF
+ return SET_ERROR(SOCK_FD_ERR(sock), ERR_WRITE_EOF);
+
+
+ if (ret < 0) {
+ // EAGAIN -> zero bytes
+ *len = 0;
+
+ } else {
+ // normal -> bytes read
+ *len = ret;
+ }
+
+ return SUCCESS;
+}
+
+err_t sock_fd_event_init (struct sock_stream *base_sock)
+{
+ struct sock_fd *sock = SOCK_FROM_BASE(base_sock, struct sock_fd);
+ err_t err;
+
+ // set nonblocking
+ if ((err = sock_fd_set_nonblock(sock, 1)))
+ return err;
+
+ // add ourselves as the event handler
+ if ((err = sock_fd_init_ev(sock, &sock_fd_event_handler, sock)))
+ return err;
+
+ // done
+ return SUCCESS;
+}
+
+err_t sock_fd_event_enable (struct sock_stream *base_sock, short mask)
+{
+ struct sock_fd *sock = SOCK_FROM_BASE(base_sock, struct sock_fd);
+
+ // implemented in sock_fd_add_event
+ return sock_fd_enable_events(sock, mask);
+}
+
+void sock_fd_init (struct sock_fd *sock, int fd)
+{
+ assert(!sock->ev_read && !sock->ev_write);
+
+ // initialize
+ sock->fd = fd;
+}
+
+err_t sock_fd_set_nonblock (struct sock_fd *sock, bool nonblock)
+{
+ // fcntl it
+ // XXX: maintain old flags?
+ if (fcntl(sock->fd, F_SETFL, nonblock ? O_NONBLOCK : 0) < 0)
+ RETURN_SET_ERROR_ERRNO(SOCK_FD_ERR(sock), ERR_FCNTL);
+
+ // ok
+ return SUCCESS;
+}
+
+err_t sock_fd_init_ev (struct sock_fd *sock, void (*ev_cb)(evutil_socket_t, short, void *), void *cb_arg)
+{
+ // require valid fd
+ assert(sock->fd >= 0);
+
+ // this is initialization
+ assert(sock->ev_read == NULL && sock->ev_write == NULL);
+
+ // create new event
+ if ((sock->ev_read = event_new(_sock_stream_ctx.ev_base, sock->fd, EV_READ, ev_cb, cb_arg)) == NULL)
+ return SET_ERROR(SOCK_FD_ERR(sock), ERR_EVENT_NEW);
+
+ if ((sock->ev_write = event_new(_sock_stream_ctx.ev_base, sock->fd, EV_WRITE, ev_cb, cb_arg)) == NULL)
+ return SET_ERROR(SOCK_FD_ERR(sock), ERR_EVENT_NEW);
+
+ // ok
+ return SUCCESS;
+}
+
+err_t sock_fd_enable_events (struct sock_fd *sock, short mask)
+{
+ // just add the appropraite events
+ if (mask & EV_READ && event_add(sock->ev_read, NULL))
+ return SET_ERROR(SOCK_FD_ERR(sock), ERR_EVENT_ADD);
+
+ if (mask & EV_WRITE && event_add(sock->ev_write, NULL))
+ return SET_ERROR(SOCK_FD_ERR(sock), ERR_EVENT_ADD);
+
+ // done
+ return SUCCESS;
+}
+
+void sock_fd_deinit_ev (struct sock_fd *sock)
+{
+ if (sock->ev_read) {
+ event_free(sock->ev_read);
+
+ sock->ev_read = NULL;
+ }
+
+ if (sock->ev_write) {
+ event_free(sock->ev_write);
+
+ sock->ev_write = NULL;
+ }
+}
+
+err_t sock_fd_close (struct sock_fd *sock)
+{
+ struct error_info *err = SOCK_FD_ERR(sock);
+
+ // no errors yet
+ RESET_ERROR(err);
+
+ // must be connected
+ assert(sock->fd >= 0);
+
+ // kill any events
+ sock_fd_deinit_ev(sock);
+
+ // close the socket itself
+ if (close(sock->fd))
+ SET_ERROR_ERRNO(err, ERR_CLOSE);
+
+ // invalidate
+ sock->fd = -1;
+
+ return ERROR_CODE(err);
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/sock_fd.h Tue Apr 07 18:09:16 2009 +0300
@@ -0,0 +1,101 @@
+#ifndef SOCK_FD_H
+#define SOCK_FD_H
+/**
+ * @file
+ *
+ * A generic sock_stream implementation for normal POSIX file descriptor based byte streams.
+ */
+#include "sock_internal.h"
+#include <event2/event.h>
+#include <stdbool.h>
+
+/**
+ * The fd-based sock_stream base implementation
+ */
+struct sock_fd {
+ /** The base struct for sock_stream_* functions */
+ struct sock_stream base;
+
+ /** The OS file descriptor */
+ int fd;
+
+ /** The IO events */
+ struct event *ev_read, *ev_write;
+
+};
+
+/**
+ * Get a sock_stream pointer from a sock_fd
+ */
+#define SOCK_FD_BASE(sock_ptr) (&(sock_ptr)->base)
+
+/**
+ * Get the sock_stream.err pointer from a sock_fd
+ */
+#define SOCK_FD_ERR(sock_ptr) SOCK_ERR(SOCK_FD_BASE(sock_ptr))
+
+
+
+/**
+ * Callback suitable for use with sock_fd_init_ev, which just invoke's the sock_stream's callbacks as appropriate.
+ */
+void sock_fd_event_handler (evutil_socket_t fd, short what, void *arg);
+
+/**
+ * sock_stream_methods::read implementation.
+ */
+err_t sock_fd_read (struct sock_stream *base_sock, void *buf, size_t *len);
+
+/**
+ * sock_stream_methods::write implementation.
+ */
+err_t sock_fd_write (struct sock_stream *base_sock, const void *buf, size_t *len);
+
+/**
+ * sock_stream_methods::event_init implementation.
+ */
+err_t sock_fd_event_init (struct sock_stream *base_sock);
+
+/**
+ * sock_stream_methods::event_enable implementation.
+ */
+err_t sock_fd_event_enable (struct sock_stream *base_sock, short mask);
+
+
+
+/**
+ * Initialize the sock_fd with the given fd, or -1, if no valid fd yet.
+ */
+void sock_fd_init (struct sock_fd *sock, int fd);
+
+/**
+ * Set the socket's nonblock mode. This should not do anything (apart from an extraneous syscall) if non-blocking
+ * mode is already set.
+ */
+err_t sock_fd_set_nonblock (struct sock_fd *sock, bool nonblock);
+
+/**
+ * Initialize sock_fd.ev_* to use the socket's fd with the given callback. The ev's are not activated yet.
+ *
+ * The sock_fd must *not* have any ev's set.
+ */
+err_t sock_fd_init_ev (struct sock_fd *sock, void (*ev_cb) (evutil_socket_t, short, void *), void *arg);
+
+/**
+ * event_add the specified ev_* events, so they are enabled and the callback will be executed.
+ */
+err_t sock_fd_enable_events (struct sock_fd *sock, short mask);
+
+/**
+ * The opposite of init_ev, this clears any set events, so that they can be re-initialized with init_ev.
+ */
+void sock_fd_deinit_ev (struct sock_fd *sock);
+
+/**
+ * Close an opened sock_fd, restoring it to a state suitable for sock_fd_init
+ */
+err_t sock_fd_close (struct sock_fd *sock);
+
+
+
+#endif
--- a/src/sock_gnutls.c Thu Apr 02 03:19:44 2009 +0300
+++ b/src/sock_gnutls.c Tue Apr 07 18:09:16 2009 +0300
@@ -82,11 +82,11 @@
err_t err;
// set nonblocking
- if ((err = sock_tcp_set_nonblock(SOCK_GNUTLS_TCP(sock), 1)))
+ if ((err = sock_fd_set_nonblock(SOCK_GNUTLS_FD(sock), true)))
return err;
// add ourselves as the event handler
- if ((err = sock_tcp_init_ev(SOCK_GNUTLS_TCP(sock), &sock_gnutls_event_handler, sock)))
+ if ((err = sock_fd_init_ev(SOCK_GNUTLS_FD(sock), &sock_gnutls_event_handler, sock)))
return err;
// ok
@@ -107,12 +107,12 @@
switch ((ret = gnutls_record_get_direction(sock->session))) {
case 0:
// read more data
- sock_tcp_add_event(SOCK_GNUTLS_TCP(sock), EV_READ);
+ sock_fd_enable_events(SOCK_GNUTLS_FD(sock), EV_READ);
break;
case 1:
// write buffer full
- sock_tcp_add_event(SOCK_GNUTLS_TCP(sock), EV_WRITE);
+ sock_fd_enable_events(SOCK_GNUTLS_FD(sock), EV_WRITE);
break;
default:
@@ -216,7 +216,7 @@
goto error;
// bind default transport functions (recv/send) to use the TCP fd
- gnutls_transport_set_ptr(sock->session, (gnutls_transport_ptr_t) (long int) sock->base_tcp.fd);
+ gnutls_transport_set_ptr(sock->session, (gnutls_transport_ptr_t) (long int) SOCK_GNUTLS_FD(sock)->fd);
// perform the handshake
if ((ERROR_EXTRA(err) = gnutls_handshake(sock->session)) < 0)
@@ -237,7 +237,7 @@
void sock_gnutls_destroy (struct sock_gnutls *sock)
{
// terminate the TCP transport
- sock_tcp_close(SOCK_GNUTLS_TCP(sock));
+ sock_fd_close(SOCK_GNUTLS_FD(sock));
// close the session rudely
// XXX: does this actually do everything we need it to? Don't want to call gnutls_bye here, since we're void...
@@ -247,4 +247,3 @@
free(sock);
}
-
--- a/src/sock_gnutls.h Thu Apr 02 03:19:44 2009 +0300
+++ b/src/sock_gnutls.h Tue Apr 07 18:09:16 2009 +0300
@@ -57,16 +57,21 @@
};
/**
- * Cast a sock_gnutls to a sock_stream.
- */
-#define SOCK_GNUTLS_BASE(sock_ptr) (&(sock_ptr)->base_tcp.base)
-
-/**
* Cast a sock_gnutls to a sock_tcp.
*/
#define SOCK_GNUTLS_TCP(sock_ptr) (&(sock_ptr)->base_tcp)
/**
+ * Cast a sock_gnutls to a sock_fd.
+ */
+#define SOCK_GNUTLS_FD(sock_ptr) SOCK_TCP_FD(SOCK_GNUTLS_TCP(sock_ptr))
+
+/**
+ * Cast a sock_gnutls to a sock_stream.
+ */
+#define SOCK_GNUTLS_BASE(sock_ptr) SOCK_TCP_BASE(SOCK_GNUTLS_TCP(sock_ptr))
+
+/**
* Get a pointer to the sock_gnutls's error_info.
*/
#define SOCK_GNUTLS_ERR(sock_ptr) SOCK_ERR(SOCK_GNUTLS_BASE(sock_ptr))
--- a/src/sock_tcp.c Thu Apr 02 03:19:44 2009 +0300
+++ b/src/sock_tcp.c Tue Apr 07 18:09:16 2009 +0300
@@ -5,116 +5,14 @@
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
-#include <unistd.h>
-#include <fcntl.h>
#include <string.h>
-#include <assert.h>
-
-/*
- * Our basic socket event handler for driving our callbacks
- */
-static void sock_tcp_event_handler (evutil_socket_t fd, short what, void *arg)
-{
- struct sock_tcp *sock = arg;
-
- (void) fd;
-
- // invoke appropriate callback
- sock_stream_invoke_callbacks(SOCK_TCP_BASE(sock), what);
-}
-
-/*
- * Our sock_stream_methods.read method
- */
-static err_t sock_tcp_read (struct sock_stream *base_sock, void *buf, size_t *len)
-{
- struct sock_tcp *sock = SOCK_FROM_BASE(base_sock, struct sock_tcp);
- int ret;
-
- // read(), and detect non-EAGAIN or EOF
- if ((ret = read(sock->fd, buf, *len)) < 0 && errno != EAGAIN)
- // unexpected error
- RETURN_SET_ERROR_ERRNO(SOCK_TCP_ERR(sock), ERR_READ);
-
- else if (ret == 0)
- // EOF
- return SET_ERROR(SOCK_TCP_ERR(sock), ERR_READ_EOF);
-
-
- if (ret < 0) {
- // EAGAIN -> zero bytes
- *len = 0;
-
- } else {
- // normal -> bytes read
- *len = ret;
- }
-
- // ok
- return SUCCESS;
-}
-
-/*
- * Our sock_stream_methods.write method
- */
-static err_t sock_tcp_write (struct sock_stream *base_sock, const void *buf, size_t *len)
-{
- struct sock_tcp *sock = SOCK_FROM_BASE(base_sock, struct sock_tcp);
- int ret;
-
- // write(), and detect non-EAGAIN or EOF
- if ((ret = write(sock->fd, buf, *len)) < 0 && errno != EAGAIN)
- // unexpected error
- RETURN_SET_ERROR_ERRNO(SOCK_TCP_ERR(sock), ERR_WRITE);
-
- else if (ret == 0)
- // EOF
- return SET_ERROR(SOCK_TCP_ERR(sock), ERR_WRITE_EOF);
-
-
- if (ret < 0) {
- // EAGAIN -> zero bytes
- *len = 0;
-
- } else {
- // normal -> bytes read
- *len = ret;
- }
-
- return SUCCESS;
-}
-
-static err_t sock_tcp_event_init (struct sock_stream *base_sock)
-{
- struct sock_tcp *sock = SOCK_FROM_BASE(base_sock, struct sock_tcp);
- err_t err;
-
- // set nonblocking
- if ((err = sock_tcp_set_nonblock(sock, 1)))
- return err;
-
- // add ourselves as the event handler
- if ((err = sock_tcp_init_ev(sock, &sock_tcp_event_handler, sock)))
- return err;
-
- // done
- return SUCCESS;
-}
-
-static err_t sock_tcp_event_enable (struct sock_stream *base_sock, short mask)
-{
- struct sock_tcp *sock = SOCK_FROM_BASE(base_sock, struct sock_tcp);
-
- // implemented in sock_tcp_add_event
- return sock_tcp_add_event(sock, mask);
-}
static void sock_tcp_release (struct sock_stream *base_sock)
{
struct sock_tcp *sock = SOCK_FROM_BASE(base_sock, struct sock_tcp);
// close and free
- sock_tcp_close(sock);
+ sock_fd_close(SOCK_TCP_FD(sock));
sock_tcp_free(sock);
}
@@ -123,15 +21,15 @@
*/
static struct sock_stream_type sock_tcp_type = {
.methods = {
- .read = &sock_tcp_read,
- .write = &sock_tcp_write,
- .event_init = &sock_tcp_event_init,
- .event_enable = &sock_tcp_event_enable,
+ .read = &sock_fd_read,
+ .write = &sock_fd_write,
+ .event_init = &sock_fd_event_init,
+ .event_enable = &sock_fd_event_enable,
.release = &sock_tcp_release,
},
};
-err_t sock_tcp_alloc (struct sock_tcp **sock_ptr)
+static err_t sock_tcp_alloc (struct sock_tcp **sock_ptr)
{
// alloc
if ((*sock_ptr = calloc(1, sizeof(**sock_ptr))) == NULL)
@@ -140,82 +38,24 @@
// initialize base with sock_tcp_type
sock_stream_init(SOCK_TCP_BASE(*sock_ptr), &sock_tcp_type);
- // invalid fds are <0
- (*sock_ptr)->fd = -1;
-
- // done
- return SUCCESS;
-}
-
-err_t sock_tcp_init_fd (struct sock_tcp *sock, int fd)
-{
- // valid fd
- assert(fd >= 0);
-
- // initialize
- sock->fd = fd;
+ // init without any fd
+ sock_fd_init(SOCK_TCP_FD(*sock_ptr), -1);
// done
return SUCCESS;
}
-err_t sock_tcp_init_ev (struct sock_tcp *sock, void (*ev_cb)(evutil_socket_t, short, void *), void *cb_arg)
-{
- // require valid fd
- assert(sock->fd >= 0);
-
- // this is initialization
- assert(sock->ev_read == NULL && sock->ev_write == NULL);
-
- // create new event
- if ((sock->ev_read = event_new(_sock_stream_ctx.ev_base, sock->fd, EV_READ, ev_cb, cb_arg)) == NULL)
- return SET_ERROR(SOCK_TCP_ERR(sock), ERR_EVENT_NEW);
-
- if ((sock->ev_write = event_new(_sock_stream_ctx.ev_base, sock->fd, EV_WRITE, ev_cb, cb_arg)) == NULL)
- return SET_ERROR(SOCK_TCP_ERR(sock), ERR_EVENT_NEW);
-
- // ok
- return SUCCESS;
-}
-
-void sock_tcp_deinit_ev (struct sock_tcp *sock)
-{
- if (sock->ev_read) {
- event_free(sock->ev_read);
-
- sock->ev_read = NULL;
- }
-
- if (sock->ev_write) {
- event_free(sock->ev_write);
-
- sock->ev_write = NULL;
- }
-}
-
err_t sock_tcp_init_socket (struct sock_tcp *sock, struct addrinfo *addr, struct error_info *err)
{
- // must not be set already
- assert(sock->fd < 0);
+ int fd;
// call socket
- if ((sock->fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol)) < 0)
+ if ((fd = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol)) < 0)
RETURN_SET_ERROR_ERRNO(err, ERR_SOCKET);
// ok
- return SUCCESS;
-}
+ sock_fd_init(SOCK_TCP_FD(sock), fd);
-err_t sock_tcp_add_event (struct sock_tcp *sock, short mask)
-{
- // just add the appropraite events
- if (mask & EV_READ && event_add(sock->ev_read, NULL))
- return SET_ERROR(SOCK_TCP_ERR(sock), ERR_EVENT_ADD);
-
- if (mask & EV_WRITE && event_add(sock->ev_write, NULL))
- return SET_ERROR(SOCK_TCP_ERR(sock), ERR_EVENT_ADD);
-
- // done
return SUCCESS;
}
@@ -257,15 +97,17 @@
*/
static void sock_tcp_connect_async_done (struct sock_tcp *sock, struct error_info *err)
{
+ struct sock_stream *sock_base = SOCK_TCP_BASE(sock);
+
// free the addrinfo
freeaddrinfo(sock->async_res);
sock->async_res = sock->async_cur = NULL;
// remove our event handler so the user can install their own
- sock_tcp_deinit_ev(sock);
+ sock_fd_deinit_ev(SOCK_TCP_FD(sock));
// ok, run callback
- SOCK_TCP_BASE(sock)->conn_cb_func(SOCK_TCP_BASE(sock), err, SOCK_TCP_BASE(sock)->conn_cb_arg);
+ sock_base->conn_cb_func(sock_base, err, sock_base->conn_cb_arg);
}
/**
@@ -303,7 +145,7 @@
error:
// close the socket
- if ((tmp = sock_tcp_close(sock)))
+ if ((tmp = sock_fd_close(SOCK_TCP_FD(sock))))
log_warn("error closing socket after connect error: %s", error_name(tmp));
// log a warning
@@ -324,20 +166,20 @@
return ERROR_CODE(err);
// then, set it up as nonblocking
- if ((ERROR_CODE(err) = sock_tcp_set_nonblock(sock, true)))
+ if ((ERROR_CODE(err) = sock_fd_set_nonblock(SOCK_TCP_FD(sock), true)))
goto error;
// then, initiate the connect
- if ((ret = connect(sock->fd, addr->ai_addr, addr->ai_addrlen)) < 0 && errno != EINPROGRESS)
+ if ((ret = connect(SOCK_TCP_FD(sock)->fd, addr->ai_addr, addr->ai_addrlen)) < 0 && errno != EINPROGRESS)
JUMP_SET_ERROR_ERRNO(err, ERR_CONNECT);
if (ret < 0) {
// ok, connect started, setup our completion callback
- if ((ERROR_CODE(err) = sock_tcp_init_ev(sock, &sock_tcp_connect_cb, sock)))
+ if ((ERROR_CODE(err) = sock_fd_init_ev(SOCK_TCP_FD(sock), &sock_tcp_connect_cb, sock)))
goto error;
// enable for write
- if ((ERROR_CODE(err) = sock_tcp_add_event(sock, EV_WRITE)))
+ if ((ERROR_CODE(err) = sock_fd_enable_events(SOCK_TCP_FD(sock), EV_WRITE)))
goto error;
// set the "current" address in case it fails and we need to try the next one
@@ -354,7 +196,7 @@
error:
// close the stuff we did open
- if ((tmp = sock_tcp_close(sock)))
+ if ((tmp = sock_fd_close(SOCK_TCP_FD(sock))))
log_warn("error closing socket after connect error: %s", error_name(tmp));
return ERROR_CODE(err);
@@ -375,7 +217,20 @@
RETURN_SET_ERROR_EXTRA(err, ERR_GETADDRINFO, ret);
// start connecting
- return sock_tcp_connect_async_continue(sock, sock->async_res, err);
+ if (sock_tcp_connect_async_continue(sock, sock->async_res, err))
+ goto error;
+
+ // ok
+ return SUCCESS;
+
+error:
+ // cleanup
+ if (sock->async_res) {
+ freeaddrinfo(sock->async_res);
+ sock->async_res = NULL;
+ }
+
+ return ERROR_CODE(err);
}
err_t sock_tcp_connect_blocking (struct sock_tcp *sock, const char *hostname, const char *service, struct error_info *err)
@@ -398,7 +253,7 @@
// try each result in turn
for (r = res; r; r = r->ai_next) {
// create the socket
- if ((sock->fd = socket(r->ai_family, r->ai_socktype, r->ai_protocol)) < 0) {
+ if ((SOCK_TCP_FD(sock)->fd = socket(r->ai_family, r->ai_socktype, r->ai_protocol)) < 0) {
// remember error
SET_ERROR_ERRNO(err, ERR_SOCKET);
@@ -407,13 +262,12 @@
}
// connect to remote address
- if (connect(sock->fd, r->ai_addr, r->ai_addrlen)) {
+ if (connect(SOCK_TCP_FD(sock)->fd, r->ai_addr, r->ai_addrlen)) {
// remember error
SET_ERROR_ERRNO(err, ERR_CONNECT);
// close/invalidate socket
- close(sock->fd);
- sock->fd = -1;
+ sock_fd_close(SOCK_TCP_FD(sock));
// skip to next one
continue;
@@ -424,7 +278,7 @@
}
// ensure we got some valid socket, else return last error code
- if (sock->fd < 0) {
+ if (SOCK_TCP_FD(sock)->fd < 0) {
// did we hit some error?
if (IS_ERROR(err))
// return last error
@@ -434,50 +288,13 @@
// no results
return SET_ERROR(err, ERR_GETADDRINFO_EMPTY);
}
-
+
// ok, done
return 0;
}
-err_t sock_tcp_set_nonblock (struct sock_tcp *sock, bool nonblock)
-{
- // fcntl it
- // XXX: maintain old flags?
- if (fcntl(sock->fd, F_SETFL, nonblock ? O_NONBLOCK : 0) < 0)
- RETURN_SET_ERROR_ERRNO(SOCK_TCP_ERR(sock), ERR_FCNTL);
-
- // ok
- return SUCCESS;
-}
-
-err_t sock_tcp_close (struct sock_tcp *sock)
-{
- struct error_info *err = SOCK_TCP_ERR(sock);
-
- // no errors yet
- RESET_ERROR(err);
-
- // must be connected
- assert(sock->fd >= 0);
-
- // kill any events
- sock_tcp_deinit_ev(sock);
-
- // close the socket itself
- if (close(sock->fd))
- SET_ERROR_ERRNO(err, ERR_CLOSE);
-
- // invalidate
- sock->fd = -1;
-
- return ERROR_CODE(err);
-}
-
void sock_tcp_free (struct sock_tcp *sock)
{
- // must not be connected
- assert(sock->fd < 0);
-
// free
free(sock);
}
--- a/src/sock_tcp.h Thu Apr 02 03:19:44 2009 +0300
+++ b/src/sock_tcp.h Tue Apr 07 18:09:16 2009 +0300
@@ -7,30 +7,29 @@
* TCP implementation of sock_stream interface.
*/
#include "sock_internal.h"
+#include "sock_fd.h"
#include <netdb.h>
-#include <stdbool.h>
/**
* Contains the base sock_stream struct, and the file descriptor
*/
struct sock_tcp {
/** The base struct for sock_stream_* functions */
- struct sock_stream base;
+ struct sock_fd base_fd;
- /** The OS file descriptor */
- int fd;
-
- /** The IO events */
- struct event *ev_read, *ev_write;
-
/** The current connect_async resolved address */
struct addrinfo *async_res, *async_cur;
};
/**
- * Get a sock_stream pointer from a sock_tcp pointer
+ * Get a sock_fd pointer from a sock_tcp pointer
*/
-#define SOCK_TCP_BASE(sock_ptr) (&(sock_ptr)->base)
+#define SOCK_TCP_FD(sock_ptr) (&(sock_ptr)->base_fd)
+
+/**
+ * Get a sock_base pointer from a sock_tcp pointer
+ */
+#define SOCK_TCP_BASE(sock_ptr) SOCK_FD_BASE(SOCK_TCP_FD(sock_ptr))
/**
* Get the sock_stream.err pointer from a sock_tcp pointer
@@ -38,26 +37,6 @@
#define SOCK_TCP_ERR(sock_ptr) SOCK_ERR(SOCK_TCP_BASE(sock_ptr))
/**
- * Allocate a new blank sock_tcp with a correctly initialized base
- */
-err_t sock_tcp_alloc (struct sock_tcp **sock_ptr);
-
-/**
- * Initialize a blank sock_tcp with a given already-existing fd
- */
-err_t sock_tcp_init_fd (struct sock_tcp *sock, int fd);
-
-/**
- * Initialize sock_tcp.ev_* to use the socket's fd with the given callback. The ev's are not activated yet.
- */
-err_t sock_tcp_init_ev (struct sock_tcp *sock, void (*ev_cb) (evutil_socket_t, short, void *), void *arg);
-
-/**
- * The opposite of init_ev, this clears any set events, so that they can be re-initialized with init_ev.
- */
-void sock_tcp_deinit_ev (struct sock_tcp *sock);
-
-/**
* Initialize a blank sock_tcp by creating a new socket (using the socket() syscall), but doesn't do anything further.
*
* This uses the ai_family, ai_socktype and ai_protocol fields from the given addrinfo.
@@ -96,21 +75,6 @@
err_t sock_tcp_connect_blocking (struct sock_tcp *sock, const char *hostname, const char *service, struct error_info *err);
/**
- * Set the socket's nonblock mode
- */
-err_t sock_tcp_set_nonblock (struct sock_tcp *sock, bool nonblock);
-
-/**
- * event_add the specified ev_* events.
- */
-err_t sock_tcp_add_event (struct sock_tcp *sock, short mask);
-
-/**
- * Close a connected sock_tcp
- */
-err_t sock_tcp_close (struct sock_tcp *sock);
-
-/**
* Free a non-connected sock_tcp
*/
void sock_tcp_free (struct sock_tcp *sock);