--- a/TODO Thu Apr 23 21:22:15 2009 +0300
+++ b/TODO Fri Apr 24 23:01:34 2009 +0300
@@ -15,9 +15,6 @@
irc_net:
* reconnect, maybe cycling servers?
-irc_chan:
- * strdup irc_chan_info strings, currently lua_irc just leaks them
-
config:
* user-defined types
* return values
@@ -29,7 +26,6 @@
* some kind of remote console
irc_log:
- * log messages sent by ourself
* recode to valid UTF8
logwatch:
--- a/src/CMakeLists.txt Thu Apr 23 21:22:15 2009 +0300
+++ b/src/CMakeLists.txt Fri Apr 24 23:01:34 2009 +0300
@@ -12,7 +12,7 @@
# define our source code modules
set (CORE_SOURCES error.c log.c str.c)
set (SOCK_SOURCES sock.c sock_fd.c sock_tcp.c sock_gnutls.c sock_test.c sock_fifo.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 (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 irc_net_connect.c)
set (LUA_SOURCES nexus_lua.c lua_objs.c lua_config.c lua_irc.c lua_func.c lua_type.c)
set (CONSOLE_SOURCES console.c lua_console.c)
--- a/src/irc_net.c Thu Apr 23 21:22:15 2009 +0300
+++ b/src/irc_net.c Fri Apr 24 23:01:34 2009 +0300
@@ -1,9 +1,11 @@
#include "irc_net.h"
+#include "irc_net_internal.h"
#include "log.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
+#include <time.h>
const char* irc_net_name (struct irc_net *net)
{
@@ -11,28 +13,6 @@
}
/**
- * Something happaned which caused our irc_conn to fail. Destroy it, and recover. XXX: somehow
- */
-static void irc_net_error (struct irc_net *net, struct error_info *err)
-{
- struct irc_chan *chan = NULL;
-
- // log an error
- log_err_info(err, "irc_conn failed");
-
- // destroy connection and set NULL
- irc_conn_destroy(net->conn);
- net->conn = NULL;
-
- // update channel state
- TAILQ_FOREACH(chan, &net->channels, net_channels) {
- // XXX: notify channel somehow
- }
-
- // XXX: reconnect?
-}
-
-/**
* Do a lookup for an irc_user with the given nickname, and return it it found, else NULL.
*
* This does not refcount anything.
@@ -65,9 +45,10 @@
// join our channels
TAILQ_FOREACH(chan, &net->channels, net_channels) {
- if ((ERROR_CODE(&err) = irc_chan_join(chan)))
+ if ((ERROR_CODE(&err) = irc_chan_join(chan))) {
// XXX: this should be some kind of irc_chan_error instead
- irc_net_error(net, &err);
+
+ }
}
}
@@ -80,7 +61,15 @@
(void) conn;
- irc_net_error(net, err);
+ // log an error
+ log_err_info(err, "irc_conn failed");
+
+ // tear down state
+ irc_net_disconnect(net, err);
+
+ // reconnect, either right away, or at the five-minute interval
+ if (irc_net_connect(net, (time(NULL) - net->connected_ts > IRC_NET_RECONNECT_INTERVAL), err))
+ log_err_info(err, "unable to reconnect");
}
/**
@@ -100,7 +89,7 @@
/**
* Our irc_conn_callbacks list
*/
-struct irc_conn_callbacks _conn_callbacks = {
+struct irc_conn_callbacks irc_net_conn_callbacks = {
.on_registered = &irc_net_conn_registered,
.on_error = &irc_net_conn_error,
.on_quit = &irc_net_conn_quit,
@@ -239,7 +228,7 @@
/**
* Our irc_cmd handler list
*/
-static struct irc_cmd_handler _cmd_handlers[] = {
+struct irc_cmd_handler irc_net_cmd_handlers[] = {
// propagate certain messages to the appropriate channel
{ "QUIT", &irc_net_on_chanuser },
{ "JOIN", &irc_net_on_chan0 },
@@ -260,113 +249,6 @@
{ NULL, NULL }
};
-/**
- * The given socket is now connected, so create the irc_conn and bind it in to us.
- *
- * If this fails, this will clean up any partial state, including sock.
- */
-static err_t irc_net_connected (struct irc_net *net, struct sock_stream *sock, struct error_info *err)
-{
- // create the irc connection state
- if (irc_conn_create(&net->conn, sock, &_conn_callbacks, net, err))
- goto error;
-
- // add our command handlers
- if ((ERROR_CODE(err) = irc_conn_add_cmd_handlers (net->conn, _cmd_handlers, net)))
- goto error;
-
- // register
- if ((ERROR_CODE(err) = irc_conn_register(net->conn, &net->info.register_info)))
- goto error;
-
- // ok
- return SUCCESS;
-
-error:
- if (!net->conn) {
- // cleanup sock ourselves
- sock_stream_release(sock);
-
- } else {
- // cleanup the partial stuff
- irc_conn_destroy(net->conn);
-
- net->conn = NULL;
- }
-
- return ERROR_CODE(err);
-}
-
-/**
- * Our sock_*_connect_async callback
- */
-static void irc_net_on_connect (struct sock_stream *sock, struct error_info *conn_err, void *arg)
-{
- struct irc_net *net = arg;
- struct error_info err;
-
- // XXX: better error handling
-
- // trap errors
- if (conn_err)
- FATAL_ERROR(conn_err, "async connect failed");
-
- // ok, yay
- if (irc_net_connected(net, sock, &err))
- FATAL_ERROR(&err, "irc_net_connected failed");
-
- // XXX: cleanup
-}
-
-/**
- * Connect and create a new irc_conn based on our irc_net_info.
- */
-static err_t irc_net_connect (struct irc_net *net, struct error_info *err)
-{
- struct irc_net_info *info = &net->info;
- struct sock_stream *sock = NULL;
-
- // sanity check
- assert(!net->conn);
-
- // connect based on what's known
- if (info->raw_sock) {
- log_debug("connected using raw socket: %p", info->raw_sock);
-
- // direct sock_stream connection
- sock = info->raw_sock;
-
- // then create the conn right away
- if (irc_net_connected(net, sock, err))
- goto error;
-
- } else if (info->ssl_cred) {
- // aquire a ref
- // NOTE: before any error handling
- sock_ssl_client_cred_get(info->ssl_cred);
-
- log_debug("connecting to [%s]:%s using SSL", info->hostname, info->service);
-
- // connect
- if (sock_ssl_connect_async(&sock, info->hostname, info->service, info->ssl_cred, &irc_net_on_connect, net, err))
- goto error;
-
- } else {
- log_debug("connecting to [%s]:%s", info->hostname, info->service);
-
- // begin async connect
- if (sock_tcp_connect_async(&sock, info->hostname, info->service, &irc_net_on_connect, net, err))
- goto error;
-
- }
-
- // ok
- return SUCCESS;
-
-error:
- return ERROR_CODE(err);
-}
-
err_t irc_net_create (struct irc_net **net_ptr, const struct irc_net_info *info, struct error_info *err)
{
struct irc_net *net;
@@ -380,9 +262,13 @@
net->info = *info;
TAILQ_INIT(&net->channels);
LIST_INIT(&net->users);
+
+ // init subsystems
+ if (irc_net_connect_init(net, err))
+ goto error;
// initial connect
- if (irc_net_connect(net, err))
+ if (irc_net_connect(net, true, err))
goto error;
// ok
@@ -424,6 +310,9 @@
// our info
if (net->info.ssl_cred)
sock_ssl_client_cred_put(net->info.ssl_cred);
+
+ // misc
+ irc_net_connect_destroy(net);
// ourselves
free(net);
--- a/src/irc_net.h Thu Apr 23 21:22:15 2009 +0300
+++ b/src/irc_net.h Fri Apr 24 23:01:34 2009 +0300
@@ -57,12 +57,24 @@
/** Our connection info */
struct irc_net_info info;
+ /** Are we currently connecting? */
+ bool connecting;
+
+ /** Are we connected? */
+ bool connected;
+
+ /** Succesfull connect time */
+ time_t connected_ts;
+
/** The list of IRC channel states */
TAILQ_HEAD(irc_net_chan_list, irc_chan) channels;
/** Our set of valid irc_user items for use with irc_chan */
LIST_HEAD(irc_net_users_list, irc_user) users;
+ /** Reconnect timer */
+ struct event *reconnect_timer;
+
/** The irc_client list */
TAILQ_ENTRY(irc_net) client_networks;
};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/irc_net_connect.c Fri Apr 24 23:01:34 2009 +0300
@@ -0,0 +1,226 @@
+
+#include "irc_net_internal.h"
+#include "log.h"
+
+#include <time.h>
+#include <assert.h>
+
+void irc_net_disconnect (struct irc_net *net, struct error_info *err)
+{
+ struct irc_chan *chan = NULL;
+
+ (void) err;
+
+ // mark
+ net->connected = false;
+
+ // destroy connection and set NULL
+ irc_conn_destroy(net->conn);
+ net->conn = NULL;
+
+ // update channel state
+ TAILQ_FOREACH(chan, &net->channels, net_channels) {
+ // XXX: notify channel somehow
+
+ }
+
+}
+
+/**
+ * We have succesfully established a connection to our server with the given sock, so create the irc_conn and bind it
+ * to us.
+ *
+ * If this fails, this will clean up any partial state, including sock.
+ */
+static err_t irc_net_connected (struct irc_net *net, struct sock_stream *sock, struct error_info *err)
+{
+ // mark state
+ net->connecting = false;
+
+ // create the irc connection state
+ if (irc_conn_create(&net->conn, sock, &irc_net_conn_callbacks, net, err))
+ goto error;
+
+ // add our command handlers
+ if ((ERROR_CODE(err) = irc_conn_add_cmd_handlers (net->conn, irc_net_cmd_handlers, net)))
+ goto error;
+
+ // register
+ if ((ERROR_CODE(err) = irc_conn_register(net->conn, &net->info.register_info)))
+ goto error;
+
+ // great, we're alive now
+ net->connected = true;
+ net->connected_ts = time(NULL);
+
+ return SUCCESS;
+
+error:
+ if (!net->conn) {
+ // cleanup sock ourselves
+ sock_stream_release(sock);
+
+ } else {
+ // cleanup the partial stuff
+ irc_conn_destroy(net->conn);
+
+ net->conn = NULL;
+ }
+
+ return ERROR_CODE(err);
+}
+
+/**
+ * Our sock_*_connect_async callback. If the connect ended up failing, then try and reconnect later. Otherwise, do
+ * irc_net_connected().
+ */
+static void irc_net_connect_cb (struct sock_stream *sock, struct error_info *conn_err, void *arg)
+{
+ struct irc_net *net = arg;
+ struct error_info err;
+
+ if (conn_err) {
+ // attempt reconnect later
+ log_err_info(conn_err, "connect failed");
+
+ if (irc_net_connect(net, false, &err))
+ log_err_info(&err, "unable to reconnect");
+
+ } else {
+ // yay
+ if (irc_net_connected(net, sock, &err))
+ log_err_info(&err, "irc_net_connected");
+
+ }
+}
+
+/**
+ * The low-level connect() implementation, connects based on irc_net::info, calling irc_net_connected/irc_net_reconnect
+ * later if this succeeds.
+ */
+static err_t irc_net_do_connect (struct irc_net *net, struct error_info *err)
+{
+ struct irc_net_info *info = &net->info;
+ struct sock_stream *sock = NULL;
+
+ // sanity check
+ assert(!net->connecting && !net->connected);
+
+ // connect based on what's known
+ if (info->raw_sock) {
+ log_debug("connected using raw socket: %p", info->raw_sock);
+
+ // direct sock_stream connection
+ sock = info->raw_sock;
+
+ // then create the conn right away
+ if (irc_net_connected(net, sock, err))
+ goto error;
+
+ } else if (info->ssl_cred) {
+ // aquire a ref
+ // NOTE: before any error handling
+ sock_ssl_client_cred_get(info->ssl_cred);
+
+ log_debug("connecting to [%s]:%s using SSL", info->hostname, info->service);
+
+ // connect
+ if (sock_ssl_connect_async(&sock, info->hostname, info->service, info->ssl_cred, &irc_net_connect_cb, net, err))
+ goto error;
+
+ net->connecting = true;
+
+ } else {
+ log_debug("connecting to [%s]:%s", info->hostname, info->service);
+
+ // begin async connect
+ if (sock_tcp_connect_async(&sock, info->hostname, info->service, &irc_net_connect_cb, net, err))
+ goto error;
+
+ net->connecting = true;
+
+ }
+
+ return SUCCESS;
+
+error:
+ return ERROR_CODE(err);
+}
+
+/**
+ * Reconnect timer callback
+ */
+static void irc_net_reconnect_timer_cb (int fd, short what, void *arg)
+{
+ struct irc_net *net = arg;
+ struct error_info err;
+
+ (void) fd;
+ (void) what;
+
+ // execute it?
+ if (irc_net_connect(net, true, &err))
+ log_err_info(&err, "unable to reconnect");
+}
+
+// XXX: to get the ev_base
+#include "sock_internal.h"
+
+/**
+ * Schedule a reconnection attempt in IRC_NET_RECONNECT_INTERVAL.
+ */
+static err_t irc_net_schedule_reconnect (struct irc_net *net, struct error_info *err)
+{
+ // ...for x seconds
+ struct timeval tv = { IRC_NET_RECONNECT_INTERVAL, 0 };
+
+ // schedule
+ if (event_add(net->reconnect_timer, &tv))
+ return SET_ERROR(err, ERR_EVENT_ADD);
+
+ // oke
+ return SUCCESS;
+}
+
+err_t irc_net_connect (struct irc_net *net, bool now, struct error_info *err)
+{
+ // attempt to reconnect now?
+ if (now) {
+ if (irc_net_do_connect(net, err))
+ // log error and continue below with schedule_reconnect
+ log_err_info(err, "reconnect failed");
+
+ else
+ // connecting, done
+ return SUCCESS;
+ }
+
+ // schedule for later
+ if (irc_net_schedule_reconnect(net, err))
+ goto error;
+
+ // ok
+ return SUCCESS;
+
+error:
+ return ERROR_CODE(err);
+}
+
+err_t irc_net_connect_init (struct irc_net *net, struct error_info *err)
+{
+ // look up the ev_base
+ struct event_base *ev_base = _sock_stream_ctx.ev_base;
+
+ // reconnect timer
+ if ((net->reconnect_timer = evtimer_new(ev_base, irc_net_reconnect_timer_cb, net)) == NULL)
+ return SET_ERROR(err, ERR_EVENT_NEW);
+
+ // ok
+ return SUCCESS;
+}
+
+void irc_net_connect_destroy (struct irc_net *net)
+{
+ event_free(net->reconnect_timer);
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/irc_net_internal.h Fri Apr 24 23:01:34 2009 +0300
@@ -0,0 +1,44 @@
+#ifndef IRC_NET_INTERNAL_H
+#define IRC_NET_INTERNAL_H
+
+/**
+ * Private irc_net interface
+ */
+#include "irc_net.h"
+
+/**
+ * Our callbacks for irc_conn
+ */
+extern struct irc_conn_callbacks irc_net_conn_callbacks;
+
+/**
+ * Our irc_conn::handlers list
+ */
+extern struct irc_cmd_handler irc_net_cmd_handlers[];
+
+/**
+ * Destroy our irc_conn, and mark ourselves as disconnected.
+ */
+void irc_net_disconnect (struct irc_net *net, struct error_info *err);
+
+/**
+ * Fixed delay between reconnection attempts in seconds
+ */
+#define IRC_NET_RECONNECT_INTERVAL (5 * 60)
+
+/**
+ * Establish a new connection to our server, either right away (if \a now give), or after IRC_NET_RECONNETC_INTERVAL.
+ */
+err_t irc_net_connect (struct irc_net *net, bool now, struct error_info *err);
+
+/**
+ * Initialize the connect state
+ */
+err_t irc_net_connect_init (struct irc_net *net, struct error_info *err);
+
+/**
+ * Destroy the connect state
+ */
+void irc_net_connect_destroy (struct irc_net *net);
+
+#endif