# HG changeset patch # User David Demelier # Date 1612126897 -3600 # Node ID 8172399babb7d9b86dc8d73e6aab17da829ceb64 # Parent a518664b20a00254d9aee6fec7c9c4749b7bb966 irccd: move more lib code diff -r a518664b20a0 -r 8172399babb7 irccd/CMakeLists.txt --- 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) diff -r a518664b20a0 -r 8172399babb7 irccd/main.c --- 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 #include #include @@ -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(); } diff -r a518664b20a0 -r 8172399babb7 irccd/peer.c --- /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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#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); +} diff -r a518664b20a0 -r 8172399babb7 irccd/peer.h --- /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 + * + * 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 + +#include + +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 */ diff -r a518664b20a0 -r 8172399babb7 irccd/transport.c --- /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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#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)); +} diff -r a518664b20a0 -r 8172399babb7 irccd/transport.h --- /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 + * + * 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 */ diff -r a518664b20a0 -r 8172399babb7 lib/CMakeLists.txt --- 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 ) diff -r a518664b20a0 -r 8172399babb7 lib/irccd/irccd.c --- 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); - } -} diff -r a518664b20a0 -r 8172399babb7 lib/irccd/irccd.h --- 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 + #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 */ diff -r a518664b20a0 -r 8172399babb7 lib/irccd/peer.c --- 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 - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#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); -} diff -r a518664b20a0 -r 8172399babb7 lib/irccd/peer.h --- 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 - * - * 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 -#include - -#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 */ diff -r a518664b20a0 -r 8172399babb7 lib/irccd/transport.c --- 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 - * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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)); -} diff -r a518664b20a0 -r 8172399babb7 lib/irccd/transport.h --- 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 - * - * 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 - -#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 */