# HG changeset patch # User Tero Marttila # Date 1236894843 -7200 # Node ID 4841f4398fd2cadfb20ef2a7cc6e0f8e2650b6c4 # Parent 7d4094eb31179d34a7dd6600e7c2a820f61bbe84 add irc_net_quit and signal handling diff -r 7d4094eb3117 -r 4841f4398fd2 Makefile --- a/Makefile Thu Mar 12 23:15:57 2009 +0200 +++ b/Makefile Thu Mar 12 23:54:03 2009 +0200 @@ -34,7 +34,7 @@ # modules module_objs = $(patsubst src/%.c,obj/%.o,$(wildcard src/$(1)/*.c)) -CORE_OBJS = obj/error.o obj/log.o obj/chain.o +CORE_OBJS = obj/error.o obj/log.o obj/chain.o obj/signals.o SOCK_OBJS = obj/sock.o obj/sock_tcp.o SOCK_TEST_OBJS = obj/sock_test.o SOCK_GNUTLS_OBJS = obj/sock_gnutls.o diff -r 7d4094eb3117 -r 4841f4398fd2 src/error.h --- a/src/error.h Thu Mar 12 23:15:57 2009 +0200 +++ b/src/error.h Thu Mar 12 23:54:03 2009 +0200 @@ -72,6 +72,11 @@ /** irc_conn errors */ _ERR_IRC_CONN = 0x000800, ERR_IRC_CONN_REGISTER_STATE, + ERR_IRC_CONN_QUIT_STATE, + + /** irc_net errors */ + _ERR_IRC_NET = 0x000900, + ERR_IRC_NET_QUIT_STATE, }; /** diff -r 7d4094eb3117 -r 4841f4398fd2 src/irc_conn.c --- a/src/irc_conn.c Thu Mar 12 23:15:57 2009 +0200 +++ b/src/irc_conn.c Thu Mar 12 23:54:03 2009 +0200 @@ -136,11 +136,19 @@ { struct irc_conn *conn = arg; - // log - log_err_info(err, "line_proto error"); - - // trash ourselves - irc_conn_set_error(conn, err); + // EOF after quit? + if (ERROR_CODE(err) == ERR_READ_EOF && conn->quitting) { + // callback + if (conn->callbacks.on_quit) + conn->callbacks.on_quit(conn, conn->cb_arg); + + } else { + // log + log_err_info(err, "line_proto error"); + + // trash ourselves + irc_conn_set_error(conn, err); + } } static struct line_proto_callbacks _lp_callbacks = { @@ -210,9 +218,7 @@ { err_t err; - // assert state if (conn->registering || conn->registered) - // XXX: stupid error code return ERR_IRC_CONN_REGISTER_STATE; // send the initial messages @@ -291,3 +297,26 @@ return irc_conn_send(conn, &line); } +err_t irc_conn_QUIT (struct irc_conn *conn, const char *message) +{ + err_t err; + + struct irc_line line = { + NULL, "QUIT", { message, NULL } + }; + + // state check + if (conn->quitting) + return ERR_IRC_CONN_QUIT_STATE; + + // try and send + if ((err = irc_conn_send(conn, &line))) + return err; + + // mark as quitting + conn->quitting = true; + + // ok + return SUCCESS; +} + diff -r 7d4094eb3117 -r 4841f4398fd2 src/irc_conn.h --- a/src/irc_conn.h Thu Mar 12 23:15:57 2009 +0200 +++ b/src/irc_conn.h Thu Mar 12 23:54:03 2009 +0200 @@ -44,8 +44,17 @@ /** * The connection has failed in some way, and can not be considered useable anymore. Sending messages won't work, * and no more messages will be received. The connection should be destroyed, probably from this callback. + * + * NOTE: Implementing this callback is mandatory */ void (*on_error) (struct irc_conn *conn, struct error_info *err, void *arg); + + /** + * The connection was closed cleanly after irc_conn_QUIT. + * + * You probably want to destroy the irc_conn now to clean up + */ + void (*on_quit) (struct irc_conn *conn, void *arg); }; /* @@ -67,6 +76,9 @@ /** Registered (as in, we have a working nickname)? */ bool registered; + + /** Quit message sent, waiting for server to close connection */ + bool quitting; // @} @@ -144,7 +156,7 @@ */ err_t irc_conn_NICK (struct irc_conn *conn, const char *nickname); -/* +/** * Send a USER message * * @param username the username to register with, may be replaced with ident reply @@ -152,14 +164,14 @@ */ err_t irc_conn_USER (struct irc_conn *conn, const char *username, const char *realname); -/* +/** * Send a PONG message to the given target * * @param target the PING source, aka. the target to send the PONG reply to */ err_t irc_conn_PONG (struct irc_conn *conn, const char *target); -/* +/** * Send a JOIN message for the given channel * * XXX: this doesn't implement the full command options @@ -168,6 +180,13 @@ */ err_t irc_conn_JOIN (struct irc_conn *conn, const char *channel); +/** + * Send a QUIT message to the server. + * + * This updates our state as disconnecting, and waits for the server to close the connection cleanly. + */ +err_t irc_conn_QUIT (struct irc_conn *conn, const char *message); + // @} #endif /* IRC_CONN_H */ diff -r 7d4094eb3117 -r 4841f4398fd2 src/irc_net.c --- a/src/irc_net.c Thu Mar 12 23:15:57 2009 +0200 +++ b/src/irc_net.c Thu Mar 12 23:54:03 2009 +0200 @@ -58,11 +58,26 @@ } /** + * Our irc_conn has quit succesfully + */ +static void irc_net_conn_quit (struct irc_conn *conn, void *arg) +{ + struct irc_net *net = arg; + + // clean up the conn + irc_conn_destroy(conn); + net->conn = NULL; + + // XXX: notify user +} + +/** * Our irc_conn_callbacks list */ struct irc_conn_callbacks _conn_callbacks = { .on_registered = &irc_net_conn_registered, .on_error = &irc_net_conn_error, + .on_quit = &irc_net_conn_quit, }; /** @@ -244,3 +259,11 @@ return NULL; } +err_t irc_net_quit (struct irc_net *net, const char *message) +{ + if (!net->conn) + return ERR_IRC_NET_QUIT_STATE; + + // send the QUIT message, and then we can wait for the reply + return irc_conn_QUIT(net->conn, message); +} diff -r 7d4094eb3117 -r 4841f4398fd2 src/irc_net.h --- a/src/irc_net.h Thu Mar 12 23:15:57 2009 +0200 +++ b/src/irc_net.h Thu Mar 12 23:54:03 2009 +0200 @@ -78,4 +78,9 @@ */ struct irc_chan* irc_net_get_chan (struct irc_net *net, const char *channel); +/** + * Quit from the IRC network, this sends a QUIT message to the server, and waits for the connection to be closed cleanly. + */ +err_t irc_net_quit (struct irc_net *net, const char *message); + #endif diff -r 7d4094eb3117 -r 4841f4398fd2 src/nexus.c --- a/src/nexus.c Thu Mar 12 23:15:57 2009 +0200 +++ b/src/nexus.c Thu Mar 12 23:54:03 2009 +0200 @@ -1,12 +1,14 @@ -#include "log.h" #include "irc_net.h" #include "irc_log.h" +#include "signals.h" +#include "log.h" #include #include #include #include +#include #include @@ -44,11 +46,49 @@ printf(" --log-channel channel to log\n"); } +/** + * Context for async nexus operation + */ +struct nexus_ctx { + /** The libevent base */ + struct event_base *ev_base; + + /** The one IRC network */ + struct irc_net *net; +}; + +void on_sigint (evutil_socket_t sig, short what, void *arg) +{ + struct nexus_ctx *ctx = arg; + + (void) sig; + (void) what; + + if (ctx->net && ctx->net->conn && !ctx->net->conn->quitting) { + log_info("Quitting..."); + + // quit it + irc_net_quit(ctx->net, "Goodbye, cruel world ;("); + + } else { + log_error("Aborting"); + + // die + if (ctx->net) { + irc_net_destroy(ctx->net); + ctx->net = NULL; + } + + // exit + event_base_loopexit(ctx->ev_base, NULL); + } +} + int main (int argc, char **argv) { int opt, option_index; - struct event_base *ev_base; - struct irc_net *net; + struct nexus_ctx ctx; + struct signals *signals; struct error_info err; struct irc_net_info net_info = { @@ -113,30 +153,41 @@ } // initialize libevent - if ((ev_base = event_base_new()) == NULL) + if ((ctx.ev_base = event_base_new()) == NULL) FATAL("event_base_new"); + + // initialize signal handlers + if ((ERROR_CODE(&err) = signals_create(&signals, ctx.ev_base))) + FATAL("signals_create"); // initialize sock module - if (sock_init(ev_base, &err)) + if (sock_init(ctx.ev_base, &err)) FATAL_ERROR(&err, "sock_init"); // the IRC network - if (irc_net_create(&net, &net_info, &err)) + if (irc_net_create(&ctx.net, &net_info, &err)) FATAL_ERROR(&err, "irc_net_create"); + + // add our signal handlers + if ( + (ERROR_CODE(&err) = signals_add(signals, SIGPIPE, &signals_ignore, signals)) + || (ERROR_CODE(&err) = signals_add(signals, SIGINT, &on_sigint, &ctx)) + ) + FATAL_ERROR(&err, "signals_add"); // logging? if (log_info.db_info || log_chan_info.channel) { // get the channel - if (log_chan_info.channel && (log_info.channel = irc_net_add_chan(net, &log_chan_info)) == NULL) + if (log_chan_info.channel && (log_info.channel = irc_net_add_chan(ctx.net, &log_chan_info)) == NULL) FATAL("irc_net_add_chan"); // init the irc_log module - if ((ERROR_CODE(&err) = irc_log_init(ev_base, &log_info))) + if ((ERROR_CODE(&err) = irc_log_init(ctx.ev_base, &log_info))) FATAL_ERROR(&err, "irc_log_init"); } // run event loop - if (event_base_dispatch(ev_base)) + if (event_base_dispatch(ctx.ev_base)) FATAL("event_base_dispatch"); // ok, no cleanup diff -r 7d4094eb3117 -r 4841f4398fd2 src/signals.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/signals.c Thu Mar 12 23:54:03 2009 +0200 @@ -0,0 +1,122 @@ +#define _GNU_SOURCE + +#include "signals.h" +#include "log.h" + +#include +#include +#include +#include + +struct signals { + /** The libevent base to use */ + struct event_base *ev_base; + + /** Information about up to MAX_SIGNALS signals */ + struct signal { + /** The event we use to handle the signal */ + struct event *ev; + + } sig_list[MAX_SIGNALS]; + + /** Number of used sig_list slots */ + int sig_count; +}; + +void signals_loopexit (int signal, short event, void *arg) +{ + struct signals *signals = arg; + + (void) event; + + log_info("caught %s: exiting", strsignal(signal)); + + if (event_base_loopexit(signals->ev_base, NULL)) + FATAL("event_base_loopexit"); +} + +void signals_ignore (int signal, short event, void *arg) +{ + struct signals *signals = arg; + + (void) signal; + (void) event; + (void) arg; + (void) signals; + + /* ignore */ +} + +err_t signals_create (struct signals **signals_ptr, struct event_base *ev_base) +{ + struct signals *signals; + + if ((signals = calloc(1, sizeof(*signals))) == NULL) + return ERR_CALLOC; + + // simple attributes + signals->ev_base = ev_base; + + // done + *signals_ptr = signals; + + return SUCCESS; +} + +err_t signals_add (struct signals *signals, int sigval, void (*sig_handler)(evutil_socket_t, short, void *), void *arg) +{ + struct signal *sig_info; + + // find our sig_info + assert(signals->sig_count < MAX_SIGNALS); + sig_info = signals->sig_list + (signals->sig_count++); + + // set up the libevent signal events + if ((sig_info->ev = evsignal_new(signals->ev_base, sigval, sig_handler, arg)) == NULL) + return ERR_EVENT_NEW; + + if (evsignal_add(sig_info->ev, NULL)) + return ERR_EVENT_ADD; + + // ok + return SUCCESS; +} + +struct signals *signals_default (struct event_base *ev_base) +{ + struct signals *signals = NULL; + + // alloc signals + if (signals_create(&signals, ev_base)) + return NULL; + + // add the set of default signals + if ( signals_add(signals, SIGPIPE, &signals_ignore, signals) + || signals_add(signals, SIGINT, &signals_loopexit, signals) + ) + goto error; + + // ok + return signals; + +error: + if (signals) + signals_free(signals); + + return NULL; +} + +void signals_free (struct signals *signals) +{ + int i; + + // free all events + for (i = 0; i < signals->sig_count; i++) { + if (evsignal_del(signals->sig_list[i].ev)) + log_warn("evsignal_del failed"); + } + + // free the info itself + free(signals); +} + diff -r 7d4094eb3117 -r 4841f4398fd2 src/signals.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/signals.h Thu Mar 12 23:54:03 2009 +0200 @@ -0,0 +1,56 @@ +#ifndef SIGNALS_H +#define SIGNALS_H + +/** + * @file + * + * Handle signals in a libevent way + */ +#include "error.h" +#include + +/** + * How many signals we can define actions for + */ +#define MAX_SIGNALS 8 + +/** + * State for a set of signals + */ +struct signals; + +/** + * Used as a handler for signals that should cause a loopexit. + */ +void signals_loopexit (int signal, short event, void *arg); + +/** + * Used to receive signals, but discard them, and continue what we were doing. + */ +void signals_ignore (int signal, short event, void *arg); + +/** + * Allocate a signals struct, acting on the given ev_base. + * + * Returns NULL on failure + */ +err_t signals_create (struct signals **signals_ptr, struct event_base *ev_base); + +/* + * Add a signal to be handled by the given signals struct with the given handler. + */ +err_t signals_add (struct signals *signals, int sigval, void (*sig_handler)(evutil_socket_t, short, void *), void *arg); + +/* + * Add a set of default signals + * SIGPIPE signals_ignore + * SIGINT signals_loopexit + */ +struct signals *signals_default (struct event_base *ev_base); + +/* + * Free the resources/handlers associated with the given signal handler + */ +void signals_free (struct signals *signals); + +#endif /* LIB_SIGNAL_H */