Mercurial > irccd
view lib/irccd/irccd.c @ 947:95201fd9ad88
irccd: servers are now linked lists
- Add reference counting to be shared with Javascript.
- Implement server-disconnect command.
author | David Demelier <markand@malikania.fr> |
---|---|
date | Sat, 16 Jan 2021 09:45:33 +0100 |
parents | 2ec05b9db2ee |
children | 21a91311c8ea |
line wrap: on
line source
/* * irccd.c -- main irccd object * * 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 <assert.h> #include <err.h> #include <errno.h> #include <poll.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include "event.h" #include "irccd.h" #include "log.h" #include "peer.h" #include "plugin.h" #include "rule.h" #include "server.h" #include "set.h" #include "transport.h" #include "util.h" struct pkg { struct pollfd *fds; size_t fdsz; }; struct defer { void (*exec)(void *); void (*data); }; struct irc irc; static int pipes[2]; static int cmp_server(const struct irc_server *s1, const struct irc_server *s2) { return strcmp(s1->name, s2->name); } static int cmp_plugin(const struct irc_plugin *p1, const struct irc_plugin *p2) { return strcmp(p1->name, p2->name); } static int cmp_peer(const struct irc_peer *p1, const struct irc_peer *p2) { return p1->fd - p2->fd; } static struct pkg prepare(void) { struct pkg pkg = {0}; size_t i = 0; pkg.fdsz += 1; /* pipe */ pkg.fdsz += 1; /* transport fd */ pkg.fdsz += irc.serversz; /* servers */ pkg.fdsz += irc.peersz; /* transport peers */ 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++]); for (size_t p = 0; p < irc.peersz; ++p) irc_peer_prepare(&irc.peers[p], &pkg.fds[i++]); for (struct irc_server *s = irc.servers; s; s = s->next) irc_server_prepare(s, &pkg.fds[i++]); return pkg; } static void invoke(const struct irc_event *ev) { for (size_t i = 0; i < irc.pluginsz; ++i) irc_plugin_handle(&irc.plugins[i], ev); } static void pipe_flush(struct pollfd *fd) { struct defer df = {0}; if (fd->fd != pipes[0] || !(fd->revents & POLLIN)) return; if (read(fd->fd, &df, sizeof (df)) != sizeof (df)) err(1, "read"); df.exec(df.data); } static void process(struct pkg *pkg) { if (poll(pkg->fds, pkg->fdsz, 1000) < 0) err(1, "poll"); /* * We can't to what file descriptors belong to so pass every file to * all services and they must check if they are associated to it or * not. */ for (size_t i = 0; i < pkg->fdsz; ++i) { struct irc_peer peer; pipe_flush(&pkg->fds[i]); #if 0 for (size_t s = 0; s < irc.serversz; ++s) irc_server_flush(irc.servers[s], &pkg->fds[i]); #endif for (struct irc_server *s = irc.servers; s; s = s->next) irc_server_flush(s, &pkg->fds[i]); /* Accept new transport client. */ if (irc_transport_flush(&pkg->fds[i], &peer)) IRC_SET_ALLOC_PUSH(&irc.peers, &irc.peersz, &peer, cmp_peer); /* Flush clients. */ for (size_t p = 0; p < irc.peersz; ) { if (!irc_peer_flush(&irc.peers[p], &pkg->fds[i])) { irc_peer_finish(&irc.peers[p]); IRC_SET_ALLOC_REMOVE(&irc.peers, &irc.peersz, &irc.peers[p]); } else ++p; } } /* * For every server, poll any kind of new event and pass them to the * plugin unless the rules explicitly disallow us to do so. */ for (struct irc_server *s = irc.servers; s; s = s->next) { struct irc_event ev; while (irc_server_poll(s, &ev)) invoke(&ev); } } static void clean(struct pkg *pkg) { free(pkg->fds); } void irc_bot_init(void) { irc_log_to_console(); if (pipe(pipes) < 0) err(1, "pipe"); } void irc_bot_add_server(struct irc_server *s) { assert(s); irc_server_incref(s); irc_server_connect(s); s->next = irc.servers; irc.servers = s; irc.serversz++; } struct irc_server * irc_bot_find_server(const char *name) { struct irc_server *s; for (s = irc.servers; s; s = s->next) if (strcmp(s->name, name) == 0) return s; return NULL; } void irc_bot_remove_server(const char *name) { struct irc_server *s, *p; if (!(s = irc_bot_find_server(name))) return; irc_server_disconnect(s); /* Don't forget to notify plugins. */ invoke(&(const struct irc_event) { .type = IRC_EVENT_DISCONNECT, .server = s }); if (s == irc.servers) irc.servers = irc.servers->next; else { /* x -> y -> z */ /* ^ */ /* s */ for (p = irc.servers->next; p->next != s; p = p->next) continue; p->next = s->next; } irc_server_decref(s); irc.serversz--; } void irc_bot_clear_servers(void) { struct irc_server *s, *next; if (!(s = irc.servers)) return; while (s) { next = s->next; irc_bot_remove_server(s->name); s = next; } } void irc_bot_add_plugin(const struct irc_plugin *p) { assert(p); IRC_SET_ALLOC_PUSH(&irc.plugins, &irc.pluginsz, p, cmp_plugin); irc_log_info("plugin %s: %s", p->name, p->description); irc_log_info("plugin %s: version %s, from %s (%s license)", p->name, p->version, p->author, p->license); irc_plugin_load(&irc.plugins[irc.pluginsz - 1]); } struct irc_plugin * irc_bot_find_plugin(const char *name) { struct irc_plugin key = {0}; strlcpy(key.name, name, sizeof (key.name)); return IRC_SET_FIND(irc.plugins, irc.pluginsz, &key, cmp_plugin); } void irc_bot_remove_plugin(const char *name) { struct irc_plugin *p; if (!(p = irc_bot_find_plugin(name))) return; irc_plugin_unload(p); irc_plugin_finish(p); IRC_SET_ALLOC_REMOVE(&irc.plugins, &irc.pluginsz, p); } bool irc_bot_insert_rule(const struct irc_rule *rule, size_t i) { assert(rule); if (irc.rulesz >= IRC_RULE_MAX) { errno = ENOMEM; return false; } if (i >= irc.rulesz) i = irc.rulesz; memmove(&irc.rules[i + 1], &irc.rules[i], sizeof (*irc.rules) * (irc.rulesz++ - i)); memcpy(&irc.rules[i], rule, sizeof (*rule)); return true; } void irc_bot_remove_rule(size_t i) { assert(i < irc.rulesz); if (i + 1 >= irc.rulesz) irc.rulesz--; else memmove(&irc.rules[i], &irc.rules[i + 1], sizeof (*irc.rules) * (irc.rulesz-- - i)); } void irc_bot_post(void (*exec)(void *), void *data) { struct defer df = { .exec = exec, .data = data }; 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); } }