From a7ba1eb15fbb902347f453070e2b76984c76cd0d Mon Sep 17 00:00:00 2001
From: Přemysl Janouch <p.janouch@gmail.com>
Date: Sat, 9 Aug 2014 00:01:33 +0200
Subject: kike: preparation for multiple listening sockets

It should be only a matter of modifying irc_setup_listen_fds() now.
---
 src/kike.c | 118 ++++++++++++++++++++++++++++++++++---------------------------
 1 file changed, 66 insertions(+), 52 deletions(-)

diff --git a/src/kike.c b/src/kike.c
index dde8d42..5a250b2 100644
--- a/src/kike.c
+++ b/src/kike.c
@@ -425,7 +425,9 @@ channel_get_mode (struct channel *self, bool disclose_secrets)
 
 struct server_context
 {
-	int listen_fd;                      ///< Listening socket FD
+	int listen_fds[1];                  ///< Listening socket FD's
+	size_t n_listen_fds;                ///< Number of listening sockets
+
 	struct client *clients;             ///< Clients
 	SSL_CTX *ssl_ctx;                   ///< SSL context
 	unsigned n_clients;                 ///< Current number of connections
@@ -450,7 +452,7 @@ struct server_context
 static void
 server_context_init (struct server_context *self)
 {
-	self->listen_fd = -1;
+	self->n_listen_fds = 0;
 	self->clients = NULL;
 	self->n_clients = 0;
 
@@ -485,8 +487,8 @@ server_context_free (struct server_context *self)
 {
 	str_map_free (&self->config);
 
-	if (self->listen_fd != -1)
-		xclose (self->listen_fd);
+	for (size_t i = 0; i < self->n_listen_fds; i++)
+		xclose (self->listen_fds[i]);
 	if (self->ssl_ctx)
 		SSL_CTX_free (self->ssl_ctx);
 
@@ -763,12 +765,14 @@ irc_initiate_quit (struct server_context *ctx)
 		if (!iter->closing_link)
 			irc_close_link (iter, "Shutting down");
 
-	ssize_t i = poller_find_by_fd (&ctx->poller, ctx->listen_fd);
-	if (soft_assert (i != -1))
-		poller_remove_at_index (&ctx->poller, i);
-	if (ctx->listen_fd != -1)
-		xclose (ctx->listen_fd);
-	ctx->listen_fd = -1;
+	for (size_t i = 0; i < ctx->n_listen_fds; i++)
+	{
+		ssize_t index = poller_find_by_fd (&ctx->poller, ctx->listen_fds[i]);
+		if (soft_assert (index != -1))
+			poller_remove_at_index (&ctx->poller, index);
+		xclose (ctx->listen_fds[i]);
+	}
+	ctx->n_listen_fds = 0;
 
 	ctx->quitting = true;
 	irc_try_finish_quit (ctx);
@@ -2501,7 +2505,7 @@ on_irc_client_available (const struct pollfd *pfd, void *user_data)
 		struct sockaddr_storage peer;
 		socklen_t peer_len = sizeof peer;
 
-		int fd = accept (ctx->listen_fd, (struct sockaddr *) &peer, &peer_len);
+		int fd = accept (pfd->fd, (struct sockaddr *) &peer, &peer_len);
 		if (fd == -1)
 		{
 			if (errno == EAGAIN)
@@ -2774,8 +2778,47 @@ irc_initialize_server_name (struct server_context *ctx, struct error **e)
 	return true;
 }
 
+static int
+irc_listen (struct addrinfo *gai_iter)
+{
+	int fd = socket (gai_iter->ai_family,
+		gai_iter->ai_socktype, gai_iter->ai_protocol);
+	if (fd == -1)
+		return -1;
+	set_cloexec (fd);
+
+	int yes = 1;
+	soft_assert (setsockopt (fd, SOL_SOCKET, SO_KEEPALIVE,
+		&yes, sizeof yes) != -1);
+	soft_assert (setsockopt (fd, SOL_SOCKET, SO_REUSEADDR,
+		&yes, sizeof yes) != -1);
+
+	char real_host[NI_MAXHOST], real_port[NI_MAXSERV];
+	real_host[0] = real_port[0] = '\0';
+	int err = getnameinfo (gai_iter->ai_addr, gai_iter->ai_addrlen,
+		real_host, sizeof real_host, real_port, sizeof real_port,
+		NI_NUMERICHOST | NI_NUMERICSERV);
+	if (err)
+		print_debug ("%s: %s", "getnameinfo", gai_strerror (err));
+
+	if (bind (fd, gai_iter->ai_addr, gai_iter->ai_addrlen))
+		print_error ("bind to %s:%s failed: %s",
+			real_host, real_port, strerror (errno));
+	else if (listen (fd, 16 /* arbitrary number */))
+		print_error ("listen at %s:%s failed: %s",
+			real_host, real_port, strerror (errno));
+	else
+	{
+		print_status ("listening at %s:%s", real_host, real_port);
+		return fd;
+	}
+
+	xclose (fd);
+	return -1;
+}
+
 static bool
-irc_listen (struct server_context *ctx, struct error **e)
+irc_setup_listen_fds (struct server_context *ctx, struct error **e)
 {
 	const char *bind_host = str_map_find (&ctx->config, "bind_host");
 	const char *bind_port = str_map_find (&ctx->config, "bind_port");
@@ -2795,56 +2838,27 @@ irc_listen (struct server_context *ctx, struct error **e)
 		return false;
 	}
 
-	int sockfd;
-	char real_host[NI_MAXHOST], real_port[NI_MAXSERV];
-
+	int fd;
 	for (gai_iter = gai_result; gai_iter; gai_iter = gai_iter->ai_next)
 	{
-		sockfd = socket (gai_iter->ai_family,
-			gai_iter->ai_socktype, gai_iter->ai_protocol);
-		if (sockfd == -1)
-			continue;
-		set_cloexec (sockfd);
-
-		int yes = 1;
-		soft_assert (setsockopt (sockfd, SOL_SOCKET, SO_KEEPALIVE,
-			&yes, sizeof yes) != -1);
-		soft_assert (setsockopt (sockfd, SOL_SOCKET, SO_REUSEADDR,
-			&yes, sizeof yes) != -1);
-
-		real_host[0] = real_port[0] = '\0';
-		err = getnameinfo (gai_iter->ai_addr, gai_iter->ai_addrlen,
-			real_host, sizeof real_host, real_port, sizeof real_port,
-			NI_NUMERICHOST | NI_NUMERICSERV);
-		if (err)
-			print_debug ("%s: %s", "getnameinfo", gai_strerror (err));
-
-		if (bind (sockfd, gai_iter->ai_addr, gai_iter->ai_addrlen))
-			print_error ("bind to %s:%s failed: %s",
-				real_host, real_port, strerror (errno));
-		else if (listen (sockfd, 16 /* arbitrary number */))
-			print_error ("listen at %s:%s failed: %s",
-				real_host, real_port, strerror (errno));
-		else
+		if (ctx->n_listen_fds >= N_ELEMENTS (ctx->listen_fds))
 			break;
+		if ((fd = irc_listen (gai_iter)) == -1)
+			continue;
 
-		xclose (sockfd);
+		ctx->listen_fds[ctx->n_listen_fds++] = fd;
+		set_blocking (fd, false);
+		poller_set (&ctx->poller, fd, POLLIN,
+			(poller_dispatcher_func) on_irc_client_available, ctx);
+		break;
 	}
-
 	freeaddrinfo (gai_result);
 
-	if (!gai_iter)
+	if (!ctx->n_listen_fds)
 	{
 		error_set (e, "network setup failed");
 		return false;
 	}
-
-	set_blocking (sockfd, false);
-	ctx->listen_fd = sockfd;
-	poller_set (&ctx->poller, ctx->listen_fd, POLLIN,
-		(poller_dispatcher_func) on_irc_client_available, ctx);
-
-	print_status ("listening at %s:%s", real_host, real_port);
 	return true;
 }
 
@@ -2979,7 +2993,7 @@ main (int argc, char *argv[])
 	 || !irc_initialize_motd (&ctx, &e)
 	 || !irc_initialize_catalog (&ctx, &e)
 	 || !irc_parse_config (&ctx, &e)
-	 || !irc_listen (&ctx, &e))
+	 || !irc_setup_listen_fds (&ctx, &e))
 	{
 		print_error ("%s", e->message);
 		error_free (e);
-- 
cgit v1.2.3-70-g09d2