# HG changeset patch # User Tero Marttila # Date 1239116956 -10800 # Node ID 9cb4051642500de65ff6c05703328d0269a2e883 # Parent 92e71129074df7a5eddfb89c458bf40342908a44 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 diff -r 92e71129074d -r 9cb405164250 src/CMakeLists.txt --- 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") diff -r 92e71129074d -r 9cb405164250 src/irc_log.c --- 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 -#include - -#include -#include - -/** - * 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 [] - */ -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 - * - * 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 [] - */ -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 [ [...]] - * - * 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 [] - */ -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" - */ -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, "[= [...]]", "set database connection info, see libpq docs" ), - CONFIG_OPT_IRC_CHAN( "channel", &irc_log_conf_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 -}; - diff -r 92e71129074d -r 9cb405164250 src/modules/irc_log.c --- /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 +#include + +#include +#include + +/** + * 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 [] + */ +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 + * + * 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 [] + */ +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 [ [...]] + * + * 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 [] + */ +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" + */ +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, "[= [...]]", "set database connection info, see libpq docs" ), + CONFIG_OPT_IRC_CHAN( "channel", &irc_log_conf_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 +}; + diff -r 92e71129074d -r 9cb405164250 src/sock.h --- 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 @@ -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 diff -r 92e71129074d -r 9cb405164250 src/sock_fd.c --- /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 +#include +#include + +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); +} + diff -r 92e71129074d -r 9cb405164250 src/sock_fd.h --- /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 +#include + +/** + * 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 diff -r 92e71129074d -r 9cb405164250 src/sock_gnutls.c --- 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); } - diff -r 92e71129074d -r 9cb405164250 src/sock_gnutls.h --- 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)) diff -r 92e71129074d -r 9cb405164250 src/sock_tcp.c --- 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 #include #include -#include -#include #include -#include - -/* - * 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); } diff -r 92e71129074d -r 9cb405164250 src/sock_tcp.h --- 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 -#include /** * 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);