--- a/src/irc_log.c Mon Mar 16 22:06:39 2009 +0200
+++ b/src/irc_log.c Mon Mar 16 23:34:05 2009 +0200
@@ -18,6 +18,14 @@
/** 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;
};
/**
@@ -29,36 +37,80 @@
/** 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)
+{
+ // schedule an evsql_destroy
+ if (evsql_destroy_next(ctx->db))
+ log_fatal("evsql_destroy_next failed");
+
+ // free ourself
+ free(ctx);
+}
+
+/**
+ * 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;
+
+ // 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_ctx *ctx = arg;
+ struct irc_log_chan *chan_ctx = arg;
err_t err;
- (void) ctx;
-
+ // check errors
if ((err = evsql_result_check(res)))
log_error("irc_log_event: %s", evsql_result_error(res));
- // ok
+ // 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 source, target, message can be NULL to insert NULLs
+ * 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_chan *chan, const struct irc_nm *source,
+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;
@@ -90,16 +142,12 @@
}
// run the INSERT
- if ((query = evsql_query_exec(ctx->db, NULL, &sql, &irc_log_on_sql_result, ctx,
- chan->net->info.network, irc_chan_name(chan),
- nickname, username, hostname,
- type, target, message
+ 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",
- chan->net->info.network, irc_chan_name(chan),
- nickname, username, hostname,
- type, target, message
+ network, channel, nickname, username, hostname, type, target, message
);
return ERR_EVSQL_QUERY_EXEC;
@@ -107,9 +155,7 @@
// ok
log_debug("%s:%s - %s!%s@%s - %s -> %s - %s",
- chan->net->info.network, irc_chan_name(chan),
- nickname, username, hostname,
- type, target, message
+ network, channel, nickname, username, hostname, type, target, message
);
return SUCCESS;
@@ -118,7 +164,7 @@
/**
* Parse the prefix into a nickmask and pass on to irc_log_event
*/
-static err_t irc_log_event_prefix (struct irc_log_ctx *ctx, struct irc_chan *chan, const char *prefix,
+static err_t irc_log_event_prefix (struct irc_log_ctx *ctx, struct irc_log_chan *chan_ctx, const char *prefix,
const char *type, const char *target, const char *message)
{
char prefix_buf[IRC_PREFIX_MAX];
@@ -130,7 +176,7 @@
return err;
// log
- return irc_log_event(ctx, chan, &nm, type, target, message);
+ return irc_log_event(ctx, chan_ctx, &nm, type, target, message);
}
/**
@@ -144,7 +190,7 @@
const char *msg = line->args[1];
- irc_log_event_prefix(chan_ctx->ctx, chan_ctx->chan, line->prefix, line->command, NULL, msg);
+ irc_log_event_prefix(chan_ctx->ctx, chan_ctx, line->prefix, line->command, NULL, msg);
}
/**
@@ -175,7 +221,7 @@
}
// log
- irc_log_event_prefix(chan_ctx->ctx, chan_ctx->chan, line->prefix, line->command, NULL, message);
+ irc_log_event_prefix(chan_ctx->ctx, chan_ctx, line->prefix, line->command, NULL, message);
}
/**
@@ -190,7 +236,7 @@
const char *target = line->args[1];
const char *msg = line->args[2];
- irc_log_event_prefix(chan_ctx->ctx, chan_ctx->chan, line->prefix, line->command, target, msg);
+ irc_log_event_prefix(chan_ctx->ctx, chan_ctx, line->prefix, line->command, target, msg);
}
/**
@@ -228,6 +274,9 @@
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);
}
@@ -247,6 +296,9 @@
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;
@@ -257,7 +309,7 @@
// log an OPEN message
// XXX: move this to when we first JOIN the channel
- if ((ERROR_CODE(err) = irc_log_event(ctx, chan_ctx->chan, NULL, "OPEN", NULL, NULL)))
+ if ((ERROR_CODE(err) = irc_log_event(ctx, chan_ctx, NULL, "OPEN", NULL, NULL)))
goto error;
// ok
@@ -273,6 +325,32 @@
}
/**
+ * 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)
@@ -286,6 +364,9 @@
// store
ctx->nexus = nexus;
+ // initialize
+ TAILQ_INIT(&ctx->channels);
+
log_info("module initialized");
// ok
@@ -360,10 +441,15 @@
/**
* Set a config option
*/
-static err_t irc_log_conf (void *mod_ctx, const char *name, char *value, struct error_info *err)
+static err_t irc_log_conf (void *_ctx, const char *name, char *value, struct error_info *err)
{
- struct irc_log_ctx *ctx = mod_ctx;
+ struct irc_log_ctx *ctx = _ctx;
+ // wrong state
+ if (ctx->unloading)
+ RETURN_SET_ERROR_STR(err, ERR_MODULE_CONF, "module is being unloaded");
+
+ // apply the config setting
if (strcmp(name, "db_info") == 0) {
return irc_log_conf_db_info(ctx, value, err);
@@ -379,10 +465,31 @@
}
/**
+ * Deinitialize, logging CLOSE events for all channels, and removing any hooks we've added
+ */
+static err_t irc_log_unload (void *_ctx)
+{
+ struct irc_log_ctx *ctx = _ctx;
+ struct irc_log_chan *chan_ctx;
+
+ // update our state to mark ourselves as unloading
+ ctx->unloading = true;
+
+ // stop logging each channel
+ TAILQ_FOREACH(chan_ctx, &ctx->channels, ctx_channels) {
+ irc_log_chan_stop(chan_ctx);
+ }
+
+ // wait for all the channels to be stopped, which will call irc_log_stopped
+ return SUCCESS;
+}
+
+/**
* The module function table
*/
struct module_funcs irc_log_funcs = {
.init = &irc_log_init,
.conf = &irc_log_conf,
+ .unload = &irc_log_unload,
};