--- 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
--- 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,
};
/**
--- 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;
+}
+
--- 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 */
--- 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);
+}
--- 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
--- 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 <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <getopt.h>
+#include <signal.h>
#include <event2/event.h>
@@ -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
--- /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 <string.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <assert.h>
+
+struct signals {
+ /** The libevent base to use */
+ struct event_base *ev_base;
+
+ /** Information about up to MAX_SIGNALS signals */
+ struct signal {
+ /** The event we use to handle the signal */
+ struct event *ev;
+
+ } sig_list[MAX_SIGNALS];
+
+ /** Number of used sig_list slots */
+ int sig_count;
+};
+
+void signals_loopexit (int signal, short event, void *arg)
+{
+ struct signals *signals = arg;
+
+ (void) event;
+
+ log_info("caught %s: exiting", strsignal(signal));
+
+ 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);
+}
+
--- /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 <event2/event.h>
+
+/**
+ * How many signals we can define actions for
+ */
+#define MAX_SIGNALS 8
+
+/**
+ * State for a set of signals
+ */
+struct signals;
+
+/**
+ * Used as a handler for signals that should cause a loopexit.
+ */
+void signals_loopexit (int signal, short event, void *arg);
+
+/**
+ * Used to receive signals, but discard them, and continue what we were doing.
+ */
+void signals_ignore (int signal, short event, void *arg);
+
+/**
+ * Allocate a signals struct, acting on the given ev_base.
+ *
+ * Returns NULL on failure
+ */
+err_t signals_create (struct signals **signals_ptr, struct event_base *ev_base);
+
+/*
+ * Add a signal to be handled by the given signals struct with the given handler.
+ */
+err_t signals_add (struct signals *signals, int sigval, void (*sig_handler)(evutil_socket_t, short, void *), void *arg);
+
+/*
+ * 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 */