changeset 980:3afd375f308b

irccd: initial reconnection support
author David Demelier <markand@malikania.fr>
date Tue, 09 Feb 2021 21:34:00 +0100
parents 489d80468925
children e4fc051e2d94
files irccd/conf.y lib/irccd/server.c lib/irccd/server.h
diffstat 3 files changed, 56 insertions(+), 14 deletions(-) [+]
line wrap: on
line diff
--- a/irccd/conf.y	Tue Feb 09 21:10:00 2021 +0100
+++ b/irccd/conf.y	Tue Feb 09 21:34:00 2021 +0100
@@ -393,6 +393,8 @@
 				$$->flags |= IRC_SERVER_FLAGS_AUTO_REJOIN;
 			else if (strcmp(s->value, "JOIN-INVITE") == 0)
 				$$->flags |= IRC_SERVER_FLAGS_JOIN_INVITE;
+			else if (strcmp(s->value, "AUTO-RECONNECT") == 0)
+				$$->flags |= IRC_SERVER_FLAGS_AUTO_RECO;
 			else
 				errx(1, "invalid server option: %s", s->value);
 		}
--- a/lib/irccd/server.c	Tue Feb 09 21:10:00 2021 +0100
+++ b/lib/irccd/server.c	Tue Feb 09 21:34:00 2021 +0100
@@ -39,6 +39,9 @@
 #include "server.h"
 #include "util.h"
 
+#define DELAY   30      /* Seconds to wait before reconnecting. */
+#define TIMEOUT 1800    /* Seconds before marking a server as dead. */
+
 static inline void
 clear_channels(struct irc_server *s, int free)
 {
@@ -58,6 +61,8 @@
 static inline void
 clear_server(struct irc_server *s)
 {
+	irc_conn_finish(&s->conn);
+
 	free(s->bufwhois.nickname);
 	free(s->bufwhois.username);
 	free(s->bufwhois.realname);
@@ -170,6 +175,22 @@
 }
 
 static void
+fail(struct irc_server *s)
+{
+	clear_channels(s, 0);
+	clear_server(s);
+
+	if (s->flags & IRC_SERVER_FLAGS_AUTO_RECO) {
+		irc_log_info("server %s: waiting %u seconds before reconnecting", s->name, DELAY);
+		s->state = IRC_SERVER_STATE_WAITING;
+	} else
+		s->state = IRC_SERVER_STATE_DISCONNECTED;
+
+	/* Time point when we lose signal from the server. */
+	s->lost_tp = time(NULL);
+}
+
+static void
 handle_connect(struct irc_server *s, struct irc_event *ev, struct irc_conn_msg *msg)
 {
 	(void)msg;
@@ -189,10 +210,10 @@
 static void
 handle_disconnect(struct irc_server *s, struct irc_event *ev)
 {
-	s->state = IRC_SERVER_STATE_NONE;
 	ev->type = IRC_EVENT_DISCONNECT;
 	ev->server = s;
 
+	fail(s);
 	irc_log_info("server %s: connection lost", s->name);
 }
 
@@ -402,7 +423,6 @@
 static void
 handle_ping(struct irc_server *s, struct irc_event *ev, struct irc_conn_msg *msg)
 {
-	(void)s;
 	(void)ev;
 	(void)msg;
 
@@ -554,6 +574,9 @@
 {
 	const struct handler *h;
 
+	/* Update last message time to detect non-notified disconnection. */
+	s->last_tp = time(NULL);
+
 	if (!(h = find_handler(msg->cmd)))
 		return;
 
@@ -621,9 +644,16 @@
 		s->conn.flags |= IRC_CONN_SSL;
 
 	if (irc_conn_connect(&s->conn) < 0)
-		s->state = IRC_SERVER_STATE_DISCONNECTED;
+		fail(s);
 	else
 		s->state = IRC_SERVER_STATE_CONNECTING;
+
+	/*
+	 * Assume the last time we received a message was now, so that
+	 * irc_server_flush don't think the server is already dead while we
+	 * didn't have any answer from it.
+	 */
+	s->last_tp = time(NULL);
 }
 
 void
@@ -633,8 +663,6 @@
 
 	s->state = IRC_SERVER_STATE_DISCONNECTED;
 
-	irc_conn_disconnect(&s->conn);
-
 	clear_channels(s, 0);
 	clear_server(s);
 }
@@ -654,12 +682,18 @@
 	assert(s);
 	assert(pfd);
 
-	if (irc_conn_flush(&s->conn, pfd) < 0)
-		return irc_server_disconnect(s);
-	if (s->conn.state != IRC_CONN_STATE_READY)
-		return;
-
 	switch (s->state) {
+	case IRC_SERVER_STATE_WAITING:
+		if (difftime(time(NULL), s->lost_tp) >= DELAY)
+			irc_server_connect(s);
+		break;
+	case IRC_SERVER_STATE_CONNECTED:
+		if (difftime(time(NULL), s->last_tp) >= TIMEOUT) {
+			irc_log_warn("server %s: no message in more than %u seconds", s->name, TIMEOUT);
+			fail(s);
+		} else if (irc_conn_flush(&s->conn, pfd) < 0)
+			return fail(s);
+		break;
 	case IRC_SERVER_STATE_CONNECTING:
 		/*
 		 * Now the conn object is ready which means the server has
@@ -687,8 +721,11 @@
 	 * this function is called again, we immediately change the state to
 	 * something else.
 	 */
-	if (s->state == IRC_SERVER_STATE_DISCONNECTED)
-		return handle_disconnect(s, ev), 1;
+	if (s->state == IRC_SERVER_STATE_DISCONNECTED) {
+		handle_disconnect(s, ev);
+		s->state = IRC_SERVER_STATE_NONE;
+		return 1;
+	}
 
 	if (irc_conn_poll(&s->conn, &msg))
 		return handle(s, ev, &msg), 1;
--- a/lib/irccd/server.h	Tue Feb 09 21:10:00 2021 +0100
+++ b/lib/irccd/server.h	Tue Feb 09 21:34:00 2021 +0100
@@ -21,6 +21,7 @@
 
 #include <sys/queue.h>
 #include <stddef.h>
+#include <time.h>
 
 #include "channel.h"
 #include "conn.h"
@@ -44,8 +45,9 @@
 	IRC_SERVER_FLAGS_SSL           = (1 << 0),
 	IRC_SERVER_FLAGS_AUTO_REJOIN   = (1 << 1),
 	IRC_SERVER_FLAGS_JOIN_INVITE   = (1 << 2),
-	IRC_SERVER_FLAGS_IPV4          = (1 << 3),
-	IRC_SERVER_FLAGS_IPV6          = (1 << 4)
+	IRC_SERVER_FLAGS_AUTO_RECO     = (1 << 3),
+	IRC_SERVER_FLAGS_IPV4          = (1 << 4),
+	IRC_SERVER_FLAGS_IPV6          = (1 << 5)
 };
 
 struct irc_server_user {
@@ -88,6 +90,7 @@
 	struct irc_event_whois bufwhois;
 	struct irc_conn conn;
 	size_t refc;
+	time_t lost_tp, last_tp;
 	LIST_ENTRY(irc_server) link;
 };