changeset 966:8172399babb7

irccd: move more lib code
author David Demelier <markand@malikania.fr>
date Sun, 31 Jan 2021 22:01:37 +0100
parents a518664b20a0
children 8becffd7617a
files irccd/CMakeLists.txt irccd/main.c irccd/peer.c irccd/peer.h irccd/transport.c irccd/transport.h lib/CMakeLists.txt lib/irccd/irccd.c lib/irccd/irccd.h lib/irccd/peer.c lib/irccd/peer.h lib/irccd/transport.c lib/irccd/transport.h
diffstat 13 files changed, 905 insertions(+), 816 deletions(-) [+]
line wrap: on
line diff
--- a/irccd/CMakeLists.txt	Fri Jan 29 15:03:23 2021 +0100
+++ b/irccd/CMakeLists.txt	Sun Jan 31 22:01:37 2021 +0100
@@ -22,6 +22,10 @@
 	SOURCES
 	${irccd_SOURCE_DIR}/dl-plugin.c
 	${irccd_SOURCE_DIR}/dl-plugin.h
+	${irccd_SOURCE_DIR}/peer.c
+	${irccd_SOURCE_DIR}/peer.h
+	${irccd_SOURCE_DIR}/transport.c
+	${irccd_SOURCE_DIR}/transport.h
 )
 
 if (IRCCD_WITH_JS)
--- a/irccd/main.c	Fri Jan 29 15:03:23 2021 +0100
+++ b/irccd/main.c	Sun Jan 31 22:01:37 2021 +0100
@@ -16,6 +16,7 @@
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
+#include <poll.h>
 #include <stdio.h>
 #include <err.h>
 
@@ -29,6 +30,40 @@
 
 #include "dl-plugin.h"
 #include "js-plugin.h"
+#include "peer.h"
+
+static struct peers peers;
+static int running = 1;
+static int has_transport;
+
+static void
+broadcast(const struct irc_event *ev)
+{
+	char buf[IRC_BUF_LEN];
+	struct peer *p;
+
+	if (!irc_event_str(ev, buf, sizeof (buf)))
+		return;
+
+	LIST_FOREACH(p, &peers, link)
+		if (p->is_watching)
+			peer_send(p, buf);
+}
+
+static inline size_t
+poll_count(void)
+{
+	struct peer *p;
+	size_t i = 1;
+
+	if (!has_transport)
+		return 0;
+
+	LIST_FOREACH(p, &peers, link)
+		++i;
+
+	return i;
+}
 
 static int
 run(int argc, char **argv)
@@ -41,6 +76,84 @@
 	return 0;
 }
 
+static inline void
+init(void)
+{
+	irc_bot_init();
+}
+
+static void
+prepare(struct pollfd *fd)
+{
+	struct peer *p;
+
+	if (!has_transport)
+		return;
+
+	transport_prepare(fd++);
+
+	LIST_FOREACH(p, &peers, link)
+		peer_prepare(p, fd++);
+}
+
+static void
+flush(const struct pollfd *fd)
+{
+	struct peer *peer, *tmp;
+
+	if (!has_transport)
+		return;
+
+	transport_flush(fd++);
+
+	LIST_FOREACH_SAFE(peer, &peers, link, tmp) {
+		if (peer_flush(peer, fd++) < 0) {
+			LIST_REMOVE(peer, link);
+			peer_finish(peer);
+		}
+	}
+}
+
+static void
+loop(void)
+{
+	struct irc_event ev;
+	struct pollfd *fds;
+	size_t botcount, owncount;
+
+	while (running) {
+		/*
+		 * Compute how much fd the bot requires and append our own
+		 * transport and peers.
+		 */
+		botcount = irc_bot_poll_count();
+		owncount = poll_count();
+		fds = irc_util_calloc(botcount + owncount, sizeof (*fds));
+
+		irc_bot_prepare(fds);
+		prepare(&fds[botcount]);
+
+		irc_bot_flush(fds);
+		flush(&fds[botcount]);
+
+		while (irc_bot_dequeue(&ev))
+			broadcast(&ev);
+
+		free(fds);
+	}
+}
+
+static inline void
+finish(void)
+{
+	struct peer *peer, *tmp;
+
+	LIST_FOREACH_SAFE(peer, &peers, link, tmp)
+		peer_finish(peer);
+
+	transport_finish();
+}
+
 int
 main(int argc, char **argv)
 {
@@ -50,14 +163,7 @@
 	if (argc > 0)
 		return run(argc, argv);
 
-	irc_bot_init();
-
-	/* TODO: temp. */
-	irc_log_set_verbose(true);
-
-	irc_bot_plugin_loader_add(dl_plugin_loader_new());
-	irc_bot_plugin_loader_add(js_plugin_loader_new());
-
-	irc_bot_plugin_find("foo");
-
+	init();
+	loop();
+	finish();
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/irccd/peer.c	Sun Jan 31 22:01:37 2021 +0100
@@ -0,0 +1,497 @@
+/*
+ * peer.c -- client connected to irccd
+ *
+ * Copyright (c) 2013-2021 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/socket.h>
+#include <ctype.h>
+#include <errno.h>
+#include <poll.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <assert.h>
+
+#include <irccd/irccd.h>
+#include <irccd/log.h>
+#include <irccd/server.h>
+#include <irccd/util.h>
+
+#include "peer.h"
+
+static size_t
+parse(char *line, const char **args, size_t max)
+{
+	size_t idx;
+
+	/* Skip command. */
+	while (*line && !isspace(*line++))
+		continue;
+
+	if (!*line)
+		return 0;
+
+	for (idx = 0; idx < max; ++idx) {
+		char *sp = strchr(line, ' ');
+
+		if (!sp || idx + 1 >= max) {
+			args[idx++] = line;
+			break;
+		}
+
+		*sp = '\0';
+		args[idx] = line;
+		line = sp + 1;
+	}
+
+	return idx;
+}
+
+static struct irc_server *
+require_server(struct peer *p, const char *id)
+{
+	struct irc_server *s;
+
+	if (!(s = irc_bot_server_find(id))) {
+		peer_send(p, "server %s not found", id);
+		return NULL;
+	}
+
+	return s;
+}
+
+static int
+ok(struct peer *p)
+{
+	peer_send(p, "OK");
+
+	return 0;
+}
+
+/*
+ * DISCONNECT [server]
+ */
+static int
+cmd_server_disconnect(struct peer *p, char *line)
+{
+	const char *args[1] = {0};
+	struct irc_server *s;
+
+	if (parse(line, args, 1) == 1) {
+		if (!(s = require_server(p, args[0])))
+			return 0;
+
+		irc_server_disconnect(s);
+	} else
+		irc_bot_server_clear();
+
+	return ok(p);
+}
+
+/*
+ * MESSAGE server channel message
+ */
+static int
+cmd_server_message(struct peer *p, char *line)
+{
+	const char *args[3] = {0};
+	struct irc_server *s;
+
+	if (parse(line, args, 3) != 3)
+		return EINVAL;
+	if (!(s = require_server(p, args[0])))
+		return 0;
+
+	irc_server_message(s, args[1], args[2]);
+
+	return ok(p);
+}
+
+/*
+ * ME server channel message
+ */
+static int
+cmd_server_me(struct peer *p, char *line)
+{
+	const char *args[3] = {0};
+	struct irc_server *s;
+
+	if (parse(line, args, 3) != 3)
+		return EINVAL;
+	if (!(s = require_server(p, args[0])))
+		return 0;
+
+	irc_server_me(s, args[1], args[2]);
+
+	return ok(p);
+}
+
+/*
+ * MODE server channel mode [limit] [user] [mask]
+ */
+static int
+cmd_server_mode(struct peer *p, char *line)
+{
+	const char *args[6] = {0};
+	struct irc_server *s;
+
+	if (parse(line, args, 6) < 3)
+		return EINVAL;
+	if (!(s = require_server(p, args[0])))
+		return 0;
+
+	irc_server_mode(s, args[1], args[2],
+	    args[3][0] ? args[3] : NULL,
+	    args[4][0] ? args[4] : NULL,
+	    args[5][0] ? args[5] : NULL
+	);
+
+	return ok(p);
+}
+
+/*
+ * NOTICE server channel message
+ */
+static int
+cmd_server_notice(struct peer *p, char *line)
+{
+	const char *args[3] = {0};
+	struct irc_server *s;
+
+	if (parse(line, args, 3) != 3)
+		return EINVAL;
+	if (!(s = require_server(p, args[0])))
+		return 0;
+
+	irc_server_notice(s, args[1], args[2]);
+
+	return ok(p);
+}
+
+/*
+ * INVITE server channel target
+ */
+static int
+cmd_server_invite(struct peer *p, char *line)
+{
+	const char *args[3] = {0};
+	struct irc_server *s;
+
+	if (parse(line, args, 3) != 3)
+		return EINVAL;
+	if (!(s = require_server(p, args[0])))
+		return 0;
+
+	irc_server_invite(s, args[1], args[2]);
+
+	return ok(p);
+}
+
+/*
+ * JOIN server channel [password]
+ */
+static int
+cmd_server_join(struct peer *p, char *line)
+{
+	const char *args[3] = {0};
+	struct irc_server *s;
+
+	if (parse(line, args, 3) < 2)
+		return EINVAL;
+	if (!(s = require_server(p, args[0])))
+		return 0;
+
+	irc_server_join(s, args[1], args[2][0] ? args[2] : NULL);
+
+	return ok(p);
+}
+
+/*
+ * KICK server channel target [reason]
+ */
+static int
+cmd_server_kick(struct peer *p, char *line)
+{
+	const char *args[4] = {0};
+	struct irc_server *s;
+
+	if (parse(line, args, 4) < 3)
+		return EINVAL;
+	if (!(s = require_server(p, args[0])))
+		return 0;
+
+	irc_server_kick(s, args[1], args[2], args[3][0] ? args[3] : NULL);
+
+	return ok(p);
+}
+
+static int
+cmd_server_list(struct peer *p, char *line)
+{
+	(void)line;
+
+	struct irc_server *s;
+	FILE *fp;
+	char *out;
+	size_t outsz;
+
+	fp = open_memstream(&out, &outsz);
+
+	fprintf(fp, "OK ");
+
+	LIST_FOREACH(s, &irc.servers, link) {
+		fprintf(fp, "%s", s->name);
+
+		if (LIST_NEXT(s, link))
+			fputc(' ', fp);
+	}
+
+	fclose(fp);
+	peer_send(p, out);
+	free(out);
+
+	return 0;
+}
+
+/*
+ * PART server channel [reason]
+ */
+static int
+cmd_server_part(struct peer *p, char *line)
+{
+	const char *args[3] = {0};
+	struct irc_server *s;
+
+	if (parse(line, args, 3) < 2)
+		return EINVAL;
+	if (!(s = require_server(p, args[0])))
+		return 0;
+
+	irc_server_part(s, args[1], args[2][0] ? args[2] : NULL);
+
+	return ok(p);
+}
+
+/*
+ * TOPIC server channel topic
+ */
+static int
+cmd_server_topic(struct peer *p, char *line)
+{
+	const char *args[3] = {0};
+	struct irc_server *s;
+
+	if (parse(line, args, 3) != 3)
+		return EINVAL;
+	if (!(s = require_server(p, args[0])))
+		return 0;
+
+	irc_server_topic(s, args[1], args[2]);
+
+	return ok(p);
+}
+
+static int
+cmd_watch(struct peer *p, char *line)
+{
+	(void)line;
+
+	p->is_watching = true;
+
+	return ok(p);
+}
+
+static const struct cmd {
+	const char *name;
+	int (*call)(struct peer *, char *);
+} cmds[] = {
+	{ "SERVER-DISCONNECT",  cmd_server_disconnect   },
+	{ "SERVER-INVITE",      cmd_server_invite       },
+	{ "SERVER-JOIN",        cmd_server_join         },
+	{ "SERVER-KICK",        cmd_server_kick         },
+	{ "SERVER-LIST",        cmd_server_list         },
+	{ "SERVER-ME",          cmd_server_me           },
+	{ "SERVER-MESSAGE",     cmd_server_message      },
+	{ "SERVER-MODE",        cmd_server_mode         },
+	{ "SERVER-NOTICE",      cmd_server_notice       },
+	{ "SERVER-PART",        cmd_server_part         },
+	{ "SERVER-TOPIC",       cmd_server_topic        },
+	{ "WATCH",              cmd_watch               }
+};
+
+static int
+cmp_cmd(const void *d1, const void *d2)
+{
+	const char *key = d1;
+	const struct cmd *cmd = d2;
+
+	return strncmp(key, cmd->name, strlen(cmd->name));
+}
+
+static const struct cmd *
+find(const char *line)
+{
+	return bsearch(line, cmds, IRC_UTIL_SIZE(cmds), sizeof (struct cmd), cmp_cmd);
+}
+
+static void
+invoke(struct peer *p, char *line)
+{
+	const struct cmd *c = find(line);
+	int er;
+
+	if (!c)
+		peer_send(p, "command not found");
+	else if ((er = c->call(p, line)) != 0)
+		peer_send(p, "%s", strerror(errno));
+}
+
+static void
+dispatch(struct peer *p)
+{
+	char *pos;
+	size_t length;
+
+	while ((pos = strstr(p->in, "\n"))) {
+		/* Turn end of the string at delimiter. */
+		*pos = 0;
+		length = pos - p->in;
+
+		if (length > 0)
+			invoke(p, p->in);
+
+		memmove(p->in, pos + 1, sizeof (p->in) - (length + 1));
+	}
+}
+
+static int
+input(struct peer *p)
+{
+	char buf[BUFSIZ + 1];
+	ssize_t nr;
+
+	if ((nr = recv(p->fd, buf, BUFSIZ, 0)) <= 0) {
+		irc_log_info("transport: client disconnect");
+		return -1;
+	}
+
+	buf[nr] = '\0';
+
+	if (strlcat(p->in, buf, sizeof (p->in)) >= sizeof (p->in)) {
+		errno = EMSGSIZE;
+		return -1;
+	}
+
+	dispatch(p);
+
+	return 0;
+}
+
+static int
+output(struct peer *p)
+{
+	ssize_t ns;
+	size_t len = strlen(p->out);
+
+	if ((ns = send(p->fd, p->out, len, 0)) < 0)
+		return -1;
+
+	if ((size_t)ns >= len)
+		memset(p->out, 0, sizeof (p->out));
+	else
+		memmove(p->out, p->out + ns, sizeof (p->out) - ns);
+
+	return 0;
+}
+
+struct peer *
+peer_new(int fd)
+{
+	struct peer *p;
+
+	p = irc_util_calloc(1, sizeof (*p));
+	p->fd = fd;
+
+	return p;
+}
+
+int
+peer_send(struct peer *p, const char *fmt, ...)
+{
+	assert(p);
+	assert(fmt);
+
+	char buf[IRC_BUF_LEN];
+	va_list ap;
+	size_t len, avail, required;
+
+	va_start(ap, fmt);
+	required = vsnprintf(buf, sizeof (buf), fmt, ap);
+	va_end(ap);
+
+	len = strlen(p->out);
+	avail = sizeof (p->out) - len;
+
+	/* Don't forget \n. */
+	if (required + 1 >= avail)
+		return -1;
+
+	strlcat(p->out, buf, sizeof (p->out));
+	strlcat(p->out, "\n", sizeof (p->out));
+
+	return 0;
+}
+
+void
+peer_prepare(struct peer *p, struct pollfd *fd)
+{
+	assert(p);
+	assert(fd);
+
+	fd->fd = p->fd;;
+	fd->events = POLLIN;
+
+	if (p->out[0])
+		fd->events |= POLLOUT;
+}
+
+int
+peer_flush(struct peer *p, const struct pollfd *fd)
+{
+	assert(p);
+	assert(fd);
+
+	if (fd->fd != p->fd)
+		return -1;
+	if (fd->revents & POLLIN && input(p) < 0)
+		return -1;
+	if (fd->revents & POLLOUT && output(p) < 0)
+		return -1;
+
+	return 0;
+}
+
+void
+peer_finish(struct peer *p)
+{
+	assert(p);
+
+	close(p->fd);
+	free(p);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/irccd/peer.h	Sun Jan 31 22:01:37 2021 +0100
@@ -0,0 +1,53 @@
+/*
+ * peer.h -- client connected to irccd
+ *
+ * Copyright (c) 2013-2021 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef IRCCD_PEER_H
+#define IRCCD_PEER_H
+
+#include <sys/queue.h>
+
+#include <irccd/limits.h>
+
+struct pollfd;
+
+struct peer {
+	int fd;
+	int is_watching;
+	char in[IRC_BUF_LEN];
+	char out[IRC_BUF_LEN];
+	LIST_ENTRY(peer) link;
+};
+
+LIST_HEAD(peers, peer);
+
+struct peer *
+peer_new(int);
+
+int
+peer_send(struct peer *, const char *, ...);
+
+void
+peer_prepare(struct peer *, struct pollfd *);
+
+int
+peer_flush(struct peer *, const struct pollfd *);
+
+void
+peer_finish(struct peer *);
+
+#endif /* !IRCCD_PEER_H */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/irccd/transport.c	Sun Jan 31 22:01:37 2021 +0100
@@ -0,0 +1,125 @@
+/*
+ * transport.c -- remote command support
+ *
+ * Copyright (c) 2013-2021 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <assert.h>
+#include <errno.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <config.h>
+
+#include <irccd/log.h>
+#include <irccd/transport.h>
+#include <irccd/util.h>
+
+#include "peer.h"
+
+static struct sockaddr_un addr;
+static int fd = -1;
+
+int
+transport_bind(const char *path)
+{
+	assert(path);
+
+	addr.sun_family = PF_LOCAL;
+
+	if (strlcpy(addr.sun_path, path, sizeof (addr.sun_path)) >= sizeof (addr.sun_path)) {
+		errno = ENAMETOOLONG;
+		goto err;
+	}
+
+	/* Silently remove the file first. */
+	unlink(addr.sun_path);
+
+	if ((fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0)
+		goto err;
+	if (bind(fd, (const struct sockaddr *)&addr, sizeof (addr)) < 0)
+		goto err;
+	if (listen(fd, 16) < 0)
+		goto err;
+
+	irc_log_info("transport: listening on %s", path);
+	irc_log_debug("transport: file descriptor %d", fd);
+
+	return true;
+
+err:
+	irc_log_warn("transport: %s: %s", path, strerror(errno));
+
+	if (fd != -1) {
+		close(fd);
+		fd = -1;
+	}
+
+	return -1;
+}
+
+void
+transport_prepare(struct pollfd *pfd)
+{
+	assert(pfd);
+
+	if (fd < 0)
+		return;
+
+	pfd->fd = fd;
+	pfd->events = POLLIN;
+}
+
+struct peer *
+transport_flush(const struct pollfd *pfd)
+{
+	assert(pfd);
+
+	struct peer *peer;
+	int newfd;
+
+	if (fd < 0 || pfd->fd != fd || !(pfd->revents & POLLIN))
+		return NULL;
+
+	if ((newfd = accept(fd, NULL, NULL)) < 0) {
+		irc_log_warn("transport: %s", strerror(errno));
+		return NULL;
+	}
+
+	peer = peer_new(newfd);
+
+	irc_log_info("transport: new client connected");
+	peer_send(peer, "IRCCD %d.%d.%d", IRCCD_VERSION_MAJOR,
+	    IRCCD_VERSION_MINOR, IRCCD_VERSION_PATCH);
+
+	return peer;
+}
+
+void
+transport_finish(void)
+{
+	/* Connection socket. */
+	if (fd != -1)
+		close(fd);
+
+	unlink(addr.sun_path);
+	memset(&addr, 0, sizeof (addr));
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/irccd/transport.h	Sun Jan 31 22:01:37 2021 +0100
@@ -0,0 +1,40 @@
+/*
+ * transport.h -- remote command support
+ *
+ * Copyright (c) 2013-2021 David Demelier <markand@malikania.fr>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef IRCCD_TRANSPORT_H
+#define IRCCD_TRANSPORT_H
+
+#include "limits.h"
+
+struct pollfd;
+
+struct peer;
+
+int
+transport_bind(const char *);
+
+void
+transport_prepare(struct pollfd *);
+
+struct peer *
+transport_flush(const struct pollfd *);
+
+void
+transport_finish(void);
+
+#endif /* !IRCCD_TRANSPORT_H */
--- a/lib/CMakeLists.txt	Fri Jan 29 15:03:23 2021 +0100
+++ b/lib/CMakeLists.txt	Sun Jan 31 22:01:37 2021 +0100
@@ -33,8 +33,6 @@
 	irccd/limits.h
 	irccd/log.c
 	irccd/log.h
-	irccd/peer.c
-	irccd/peer.h
 	irccd/plugin.c
 	irccd/plugin.h
 	irccd/rule.c
@@ -43,8 +41,6 @@
 	irccd/server.h
 	irccd/subst.c
 	irccd/subst.h
-	irccd/transport.c
-	irccd/transport.h
 	irccd/util.c
 	irccd/util.h
 )
--- a/lib/irccd/irccd.c	Fri Jan 29 15:03:23 2021 +0100
+++ b/lib/irccd/irccd.c	Sun Jan 31 22:01:37 2021 +0100
@@ -28,18 +28,11 @@
 #include "event.h"
 #include "irccd.h"
 #include "log.h"
-#include "peer.h"
 #include "plugin.h"
 #include "rule.h"
 #include "server.h"
-#include "transport.h"
 #include "util.h"
 
-struct pkg {
-	struct pollfd *fds;
-	size_t fdsz;
-};
-
 struct defer {
 	void (*exec)(void *);
 	void *data;
@@ -47,7 +40,6 @@
 
 struct irc irc = {
 	.servers = LIST_HEAD_INITIALIZER(),
-	.peers = LIST_HEAD_INITIALIZER(),
 	.plugins = LIST_HEAD_INITIALIZER(),
 	.rules = TAILQ_HEAD_INITIALIZER(irc.rules)
 };
@@ -146,61 +138,6 @@
 	}
 }
 
-static inline size_t
-pollable(void)
-{
-	const struct irc_server *s;
-	const struct irc_peer *p;
-	size_t i = 2;                   /* pipe + transport. */
-
-	LIST_FOREACH(s, &irc.servers, link)
-		++i;
-	LIST_FOREACH(p, &irc.peers, link)
-		++i;
-
-	return i;
-}
-
-static struct pkg
-prepare(void)
-{
-	struct irc_peer *p;
-	struct irc_server *s;
-	struct pkg pkg = {0};
-	size_t i = 0;
-
-	pkg.fdsz = pollable();
-	pkg.fds = irc_util_calloc(pkg.fdsz, sizeof (*pkg.fds));
-
-	/* pipe */
-	pkg.fds[i].fd = pipes[0];
-	pkg.fds[i++].events = POLLIN;
-
-	/* transport */
-	irc_transport_prepare(&pkg.fds[i++]);
-
-	LIST_FOREACH(p, &irc.peers, link)
-		irc_peer_prepare(p, &pkg.fds[i++]);
-	LIST_FOREACH(s, &irc.servers, link)
-		irc_server_prepare(s, &pkg.fds[i++]);
-
-	return pkg;
-}
-
-static inline void
-broadcast(const struct irc_event *ev)
-{
-	char buf[IRC_BUF_LEN];
-	struct irc_peer *p;
-
-	if (!irc_event_str(ev, buf, sizeof (buf)))
-		return;
-
-	LIST_FOREACH(p, &irc.peers, link)
-		if (p->is_watching)
-			irc_peer_send(p, buf);
-}
-
 static void
 invoke(struct irc_event *ev)
 {
@@ -232,7 +169,7 @@
 }
 
 static void
-pipe_flush(struct pollfd *fd)
+pipe_flush(const struct pollfd *fd)
 {
 	struct defer df = {0};
 
@@ -245,6 +182,7 @@
 	df.exec(df.data);
 }
 
+#if 0
 static void
 process(struct pkg *pkg)
 {
@@ -292,11 +230,7 @@
 	}
 }
 
-static inline void
-clean(struct pkg *pkg)
-{
-	free(pkg->fds);
-}
+#endif
 
 static inline size_t
 rulescount(void)
@@ -509,6 +443,59 @@
 	TAILQ_INIT(&irc.rules);
 }
 
+size_t
+irc_bot_poll_count(void)
+{
+	size_t i = 1;
+	struct irc_server *s;
+
+	LIST_FOREACH(s, &irc.servers, link)
+		++i;
+
+	return i;
+}
+
+void
+irc_bot_prepare(struct pollfd *fds)
+{
+	assert(fds);
+
+	struct irc_server *s;
+	size_t i = 1;
+
+	fds[0].fd = pipes[0];
+	fds[0].events = POLLIN;
+
+	LIST_FOREACH(s, &irc.servers, link)
+		irc_server_prepare(s, &fds[i++]);
+}
+
+void
+irc_bot_flush(const struct pollfd *fds)
+{
+	assert(fds);
+
+	struct irc_server *s;
+	size_t i = 1;
+
+	pipe_flush(&fds[0]);
+
+	LIST_FOREACH(s, &irc.servers, link)
+		irc_server_flush(s, &fds[i++]);
+}
+
+int
+irc_bot_dequeue(struct irc_event *ev)
+{
+	struct irc_server *s;
+
+	LIST_FOREACH(s, &irc.servers, link)
+		if (irc_server_poll(s, ev))
+			return 1;
+
+	return 0;
+}
+
 void
 irc_bot_post(void (*exec)(void *), void *data)
 {
@@ -520,15 +507,3 @@
 	if (write(pipes[1], &df, sizeof (df)) != sizeof (df))
 		err(1, "write");
 }
-
-void
-irc_bot_run(void)
-{
-	struct pkg pkg;
-
-	for (;;) {
-		pkg = prepare();
-		process(&pkg);
-		clean(&pkg);
-	}
-}
--- a/lib/irccd/irccd.h	Fri Jan 29 15:03:23 2021 +0100
+++ b/lib/irccd/irccd.h	Sun Jan 31 22:01:37 2021 +0100
@@ -19,14 +19,14 @@
 #ifndef IRCCD_H
 #define IRCCD_H
 
-#include "peer.h"
+#include <stddef.h>
+
 #include "plugin.h"
 #include "rule.h"
 #include "server.h"
 
 extern struct irc {
 	struct irc_server_list servers;
-	struct irc_peer_list peers;
 	struct irc_plugin_list plugins;
 	struct irc_plugin_loader_list plugin_loaders;
 	struct irc_rule_list rules;
@@ -71,10 +71,19 @@
 void
 irc_bot_rule_clear(void);
 
+size_t
+irc_bot_poll_count(void);
+
+void
+irc_bot_prepare(struct pollfd *);
+
+void
+irc_bot_flush(const struct pollfd *);
+
+int
+irc_bot_dequeue(struct irc_event *);
+
 void
 irc_bot_post(void (*)(void *), void *);
 
-void
-irc_bot_run(void);
-
 #endif /* !IRCCD_H */
--- a/lib/irccd/peer.c	Fri Jan 29 15:03:23 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,497 +0,0 @@
-/*
- * peer.c -- client connected to irccd
- *
- * Copyright (c) 2013-2021 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <sys/socket.h>
-#include <ctype.h>
-#include <errno.h>
-#include <poll.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <assert.h>
-
-#include "irccd.h"
-#include "log.h"
-#include "peer.h"
-#include "server.h"
-#include "util.h"
-
-static size_t
-parse(char *line, const char **args, size_t max)
-{
-	size_t idx;
-
-	/* Skip command. */
-	while (*line && !isspace(*line++))
-		continue;
-
-	if (!*line)
-		return 0;
-
-	for (idx = 0; idx < max; ++idx) {
-		char *sp = strchr(line, ' ');
-
-		if (!sp || idx + 1 >= max) {
-			args[idx++] = line;
-			break;
-		}
-
-		*sp = '\0';
-		args[idx] = line;
-		line = sp + 1;
-	}
-
-	return idx;
-}
-
-static struct irc_server *
-require_server(struct irc_peer *p, const char *id)
-{
-	struct irc_server *s;
-
-	if (!(s = irc_bot_server_find(id))) {
-		irc_peer_send(p, "server %s not found", id);
-		return NULL;
-	}
-
-	return s;
-}
-
-static int
-ok(struct irc_peer *p)
-{
-	irc_peer_send(p, "OK");
-
-	return 0;
-}
-
-/*
- * DISCONNECT [server]
- */
-static int
-cmd_server_disconnect(struct irc_peer *p, char *line)
-{
-	const char *args[1] = {0};
-	struct irc_server *s;
-
-	if (parse(line, args, 1) == 1) {
-		if (!(s = require_server(p, args[0])))
-			return 0;
-
-		irc_server_disconnect(s);
-	} else
-		irc_bot_server_clear();
-
-	return ok(p);
-}
-
-/*
- * MESSAGE server channel message
- */
-static int
-cmd_server_message(struct irc_peer *p, char *line)
-{
-	const char *args[3] = {0};
-	struct irc_server *s;
-
-	if (parse(line, args, 3) != 3)
-		return EINVAL;
-	if (!(s = require_server(p, args[0])))
-		return 0;
-
-	irc_server_message(s, args[1], args[2]);
-
-	return ok(p);
-}
-
-/*
- * ME server channel message
- */
-static int
-cmd_server_me(struct irc_peer *p, char *line)
-{
-	const char *args[3] = {0};
-	struct irc_server *s;
-
-	if (parse(line, args, 3) != 3)
-		return EINVAL;
-	if (!(s = require_server(p, args[0])))
-		return 0;
-
-	irc_server_me(s, args[1], args[2]);
-
-	return ok(p);
-}
-
-/*
- * MODE server channel mode [limit] [user] [mask]
- */
-static int
-cmd_server_mode(struct irc_peer *p, char *line)
-{
-	const char *args[6] = {0};
-	struct irc_server *s;
-
-	if (parse(line, args, 6) < 3)
-		return EINVAL;
-	if (!(s = require_server(p, args[0])))
-		return 0;
-
-	irc_server_mode(s, args[1], args[2],
-	    args[3][0] ? args[3] : NULL,
-	    args[4][0] ? args[4] : NULL,
-	    args[5][0] ? args[5] : NULL
-	);
-
-	return ok(p);
-}
-
-/*
- * NOTICE server channel message
- */
-static int
-cmd_server_notice(struct irc_peer *p, char *line)
-{
-	const char *args[3] = {0};
-	struct irc_server *s;
-
-	if (parse(line, args, 3) != 3)
-		return EINVAL;
-	if (!(s = require_server(p, args[0])))
-		return 0;
-
-	irc_server_notice(s, args[1], args[2]);
-
-	return ok(p);
-}
-
-/*
- * INVITE server channel target
- */
-static int
-cmd_server_invite(struct irc_peer *p, char *line)
-{
-	const char *args[3] = {0};
-	struct irc_server *s;
-
-	if (parse(line, args, 3) != 3)
-		return EINVAL;
-	if (!(s = require_server(p, args[0])))
-		return 0;
-
-	irc_server_invite(s, args[1], args[2]);
-
-	return ok(p);
-}
-
-/*
- * JOIN server channel [password]
- */
-static int
-cmd_server_join(struct irc_peer *p, char *line)
-{
-	const char *args[3] = {0};
-	struct irc_server *s;
-
-	if (parse(line, args, 3) < 2)
-		return EINVAL;
-	if (!(s = require_server(p, args[0])))
-		return 0;
-
-	irc_server_join(s, args[1], args[2][0] ? args[2] : NULL);
-
-	return ok(p);
-}
-
-/*
- * KICK server channel target [reason]
- */
-static int
-cmd_server_kick(struct irc_peer *p, char *line)
-{
-	const char *args[4] = {0};
-	struct irc_server *s;
-
-	if (parse(line, args, 4) < 3)
-		return EINVAL;
-	if (!(s = require_server(p, args[0])))
-		return 0;
-
-	irc_server_kick(s, args[1], args[2], args[3][0] ? args[3] : NULL);
-
-	return ok(p);
-}
-
-static int
-cmd_server_list(struct irc_peer *p, char *line)
-{
-	(void)line;
-
-	struct irc_server *s;
-	FILE *fp;
-	char *out;
-	size_t outsz;
-
-	fp = open_memstream(&out, &outsz);
-
-	fprintf(fp, "OK ");
-
-	LIST_FOREACH(s, &irc.servers, link) {
-		fprintf(fp, "%s", s->name);
-
-		if (LIST_NEXT(s, link))
-			fputc(' ', fp);
-	}
-
-	fclose(fp);
-	irc_peer_send(p, out);
-	free(out);
-
-	return 0;
-}
-
-/*
- * PART server channel [reason]
- */
-static int
-cmd_server_part(struct irc_peer *p, char *line)
-{
-	const char *args[3] = {0};
-	struct irc_server *s;
-
-	if (parse(line, args, 3) < 2)
-		return EINVAL;
-	if (!(s = require_server(p, args[0])))
-		return 0;
-
-	irc_server_part(s, args[1], args[2][0] ? args[2] : NULL);
-
-	return ok(p);
-}
-
-/*
- * TOPIC server channel topic
- */
-static int
-cmd_server_topic(struct irc_peer *p, char *line)
-{
-	const char *args[3] = {0};
-	struct irc_server *s;
-
-	if (parse(line, args, 3) != 3)
-		return EINVAL;
-	if (!(s = require_server(p, args[0])))
-		return 0;
-
-	irc_server_topic(s, args[1], args[2]);
-
-	return ok(p);
-}
-
-static int
-cmd_watch(struct irc_peer *p, char *line)
-{
-	(void)line;
-
-	p->is_watching = true;
-
-	return ok(p);
-}
-
-static const struct cmd {
-	const char *name;
-	int (*call)(struct irc_peer *, char *);
-} cmds[] = {
-	{ "SERVER-DISCONNECT",  cmd_server_disconnect   },
-	{ "SERVER-INVITE",      cmd_server_invite       },
-	{ "SERVER-JOIN",        cmd_server_join         },
-	{ "SERVER-KICK",        cmd_server_kick         },
-	{ "SERVER-LIST",        cmd_server_list         },
-	{ "SERVER-ME",          cmd_server_me           },
-	{ "SERVER-MESSAGE",     cmd_server_message      },
-	{ "SERVER-MODE",        cmd_server_mode         },
-	{ "SERVER-NOTICE",      cmd_server_notice       },
-	{ "SERVER-PART",        cmd_server_part         },
-	{ "SERVER-TOPIC",       cmd_server_topic        },
-	{ "WATCH",              cmd_watch               }
-};
-
-static int
-cmp_cmd(const void *d1, const void *d2)
-{
-	const char *key = d1;
-	const struct cmd *cmd = d2;
-
-	return strncmp(key, cmd->name, strlen(cmd->name));
-}
-
-static const struct cmd *
-find(const char *line)
-{
-	return bsearch(line, cmds, IRC_UTIL_SIZE(cmds), sizeof (struct cmd), cmp_cmd);
-}
-
-static void
-invoke(struct irc_peer *p, char *line)
-{
-	const struct cmd *c = find(line);
-	int er;
-
-	if (!c)
-		irc_peer_send(p, "command not found");
-	else if ((er = c->call(p, line)) != 0)
-		irc_peer_send(p, "%s", strerror(errno));
-}
-
-static void
-dispatch(struct irc_peer *p)
-{
-	char *pos;
-	size_t length;
-
-	while ((pos = strstr(p->in, "\n"))) {
-		/* Turn end of the string at delimiter. */
-		*pos = 0;
-		length = pos - p->in;
-
-		if (length > 0)
-			invoke(p, p->in);
-
-		memmove(p->in, pos + 1, sizeof (p->in) - (length + 1));
-	}
-}
-
-static bool
-input(struct irc_peer *p)
-{
-	char buf[BUFSIZ + 1];
-	ssize_t nr;
-
-	if ((nr = recv(p->fd, buf, BUFSIZ, 0)) <= 0) {
-		irc_log_info("transport: client disconnect");
-		return false;
-	}
-
-	buf[nr] = '\0';
-
-	if (strlcat(p->in, buf, sizeof (p->in)) >= sizeof (p->in)) {
-		errno = EMSGSIZE;
-		return false;
-	}
-
-	dispatch(p);
-
-	return true;
-}
-
-static bool
-output(struct irc_peer *p)
-{
-	ssize_t ns;
-	size_t len = strlen(p->out);
-
-	if ((ns = send(p->fd, p->out, len, 0)) < 0)
-		return false;
-
-	if ((size_t)ns >= len)
-		memset(p->out, 0, sizeof (p->out));
-	else
-		memmove(p->out, p->out + ns, sizeof (p->out) - ns);
-
-	return true;
-}
-
-struct irc_peer *
-irc_peer_new(int fd)
-{
-	struct irc_peer *p;
-
-	p = irc_util_calloc(1, sizeof (*p));
-	p->fd = fd;
-
-	return p;
-}
-
-bool
-irc_peer_send(struct irc_peer *p, const char *fmt, ...)
-{
-	assert(p);
-	assert(fmt);
-
-	char buf[IRC_BUF_LEN];
-	va_list ap;
-	size_t len, avail, required;
-
-	va_start(ap, fmt);
-	required = vsnprintf(buf, sizeof (buf), fmt, ap);
-	va_end(ap);
-
-	len = strlen(p->out);
-	avail = sizeof (p->out) - len;
-
-	/* Don't forget \n. */
-	if (required + 1 >= avail)
-		return false;
-
-	strlcat(p->out, buf, sizeof (p->out));
-	strlcat(p->out, "\n", sizeof (p->out));
-
-	return true;
-}
-
-void
-irc_peer_prepare(struct irc_peer *p, struct pollfd *fd)
-{
-	assert(p);
-	assert(fd);
-
-	fd->fd = p->fd;;
-	fd->events = POLLIN;
-
-	if (p->out[0])
-		fd->events |= POLLOUT;
-}
-
-bool
-irc_peer_flush(struct irc_peer *p, const struct pollfd *fd)
-{
-	assert(p);
-	assert(fd);
-
-	if (fd->fd != p->fd)
-		return true;
-
-	if (fd->revents & POLLIN && !input(p))
-		return false;
-	if (fd->revents & POLLOUT && !output(p))
-		return false;
-
-	return true;
-}
-
-void
-irc_peer_finish(struct irc_peer *p)
-{
-	assert(p);
-
-	close(p->fd);
-	free(p);
-}
--- a/lib/irccd/peer.h	Fri Jan 29 15:03:23 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * peer.h -- client connected to irccd
- *
- * Copyright (c) 2013-2021 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef IRCCD_PEER_H
-#define IRCCD_PEER_H
-
-#include <sys/queue.h>
-#include <stdbool.h>
-
-#include "limits.h"
-
-struct pollfd;
-
-struct irc_peer {
-	int fd;
-	bool is_watching;
-	char in[IRC_BUF_LEN];
-	char out[IRC_BUF_LEN];
-	LIST_ENTRY(irc_peer) link;
-};
-
-LIST_HEAD(irc_peer_list, irc_peer);
-
-struct irc_peer *
-irc_peer_new(int);
-
-bool
-irc_peer_send(struct irc_peer *, const char *, ...);
-
-void
-irc_peer_prepare(struct irc_peer *, struct pollfd *);
-
-bool
-irc_peer_flush(struct irc_peer *, const struct pollfd *);
-
-void
-irc_peer_finish(struct irc_peer *);
-
-#endif /* !IRCCD_PEER_H */
--- a/lib/irccd/transport.c	Fri Jan 29 15:03:23 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,123 +0,0 @@
-/*
- * transport.c -- remote command support
- *
- * Copyright (c) 2013-2021 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <assert.h>
-#include <errno.h>
-#include <poll.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/un.h>
-#include <unistd.h>
-
-#include "config.h"
-#include "log.h"
-#include "transport.h"
-#include "peer.h"
-#include "util.h"
-
-static struct sockaddr_un addr;
-static int fd = -1;
-
-bool
-irc_transport_bind(const char *path)
-{
-	assert(path);
-
-	addr.sun_family = PF_LOCAL;
-
-	if (strlcpy(addr.sun_path, path, sizeof (addr.sun_path)) >= sizeof (addr.sun_path)) {
-		errno = ENAMETOOLONG;
-		goto err;
-	}
-
-	/* Silently remove the file first. */
-	unlink(addr.sun_path);
-
-	if ((fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0)
-		goto err;
-	if (bind(fd, (const struct sockaddr *)&addr, sizeof (addr)) < 0)
-		goto err;
-	if (listen(fd, 16) < 0)
-		goto err;
-
-	irc_log_info("transport: listening on %s", path);
-	irc_log_debug("transport: file descriptor %d", fd);
-
-	return true;
-
-err:
-	irc_log_warn("transport: %s: %s", path, strerror(errno));
-
-	if (fd != -1) {
-		close(fd);
-		fd = -1;
-	}
-
-	return -1;
-}
-
-void
-irc_transport_prepare(struct pollfd *pfd)
-{
-	assert(pfd);
-
-	if (fd < 0)
-		return;
-
-	pfd->fd = fd;
-	pfd->events = POLLIN;
-}
-
-struct irc_peer *
-irc_transport_flush(const struct pollfd *pfd)
-{
-	assert(pfd);
-
-	struct irc_peer *peer;
-	int newfd;
-
-	if (fd < 0 || pfd->fd != fd || !(pfd->revents & POLLIN))
-		return NULL;
-
-	if ((newfd = accept(fd, NULL, NULL)) < 0) {
-		irc_log_warn("transport: %s", strerror(errno));
-		return NULL;
-	}
-
-	peer = irc_peer_new(newfd);
-
-	irc_log_info("transport: new client connected");
-	irc_peer_send(peer, "IRCCD %d.%d.%d", IRCCD_VERSION_MAJOR,
-	    IRCCD_VERSION_MINOR, IRCCD_VERSION_PATCH);
-
-	return peer;
-}
-
-void
-irc_transport_finish(void)
-{
-	/* Connection socket. */
-	if (fd != -1)
-		close(fd);
-
-	unlink(addr.sun_path);
-	memset(&addr, 0, sizeof (addr));
-}
--- a/lib/irccd/transport.h	Fri Jan 29 15:03:23 2021 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,42 +0,0 @@
-/*
- * transport.h -- remote command support
- *
- * Copyright (c) 2013-2021 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef IRCCD_TRANSPORT_H
-#define IRCCD_TRANSPORT_H
-
-#include <stdbool.h>
-
-#include "limits.h"
-
-struct pollfd;
-
-struct irc_peer;
-
-bool
-irc_transport_bind(const char *);
-
-void
-irc_transport_prepare(struct pollfd *);
-
-struct irc_peer *
-irc_transport_flush(const struct pollfd *);
-
-void
-irc_transport_finish(void);
-
-#endif /* !IRCCD_TRANSPORT_H */