ticl

tiny irc channel linker
git clone git://git.ircforever.org/ticl
Log | Files | Refs | Submodules | README | LICENSE

commit 202447793107a549c7e418dc3b2208caa89a0bb3
parent 375f68cba6f87f824a01f604b65057138c754a43
Author: libredev <libredev@ircforever.org>
Date:   Mon, 13 Feb 2023 13:46:51 +0530

cleaned up the code

Diffstat:
Mconfig.mk | 3---
Mhtable.c | 305++++++++++++++++++++++++++++++++++++++-----------------------------------------
Mhtable.h | 65++++++++++++++++++++++++++++++++++++-----------------------------
Mmain.c | 986++++++++++++++++++++++++++++++++++++-------------------------------------------
Mutil.c | 222++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
5 files changed, 776 insertions(+), 805 deletions(-)

diff --git a/config.mk b/config.mk @@ -6,9 +6,6 @@ VERSION != date '+%Y-%m-%d' PREFIX = /usr/local MANPREFIX = $(PREFIX)/share/man -# OpenBSD (comment) -LIBS = -lbsd - CPPFLAGS = -D_DEFAULT_SOURCE -DVERSION=\"$(VERSION)\" CFLAGS = -g -std=c89 -Wall -Wextra -pedantic -Wfatal-errors -Wconversion\ -Wstrict-prototypes -Wold-style-definition $(CPPFLAGS) diff --git a/htable.c b/htable.c @@ -3,70 +3,25 @@ * See COPYING file for more information. */ -#include <errno.h> #include <stdlib.h> #include <stdio.h> -#include <string.h> #include "htable.h" -static unsigned int -hash(Htable *ht, void *key) -{ - unsigned int i; - unsigned int sum = 0; - - /* very simple hash function */ - for (i = 0; i < (unsigned int)ht->keylenfn(key); i++) { - sum += ((unsigned char *)key)[i] * (i + 1); - } - return (sum % ht->cap); -} - -Htable * -htcreate(KeyLenFn *keylenfn, KeyCmpFn *keycmpfn, FreeKeyFn *freekeyfn, FreeValFn *freevalfn, unsigned int cap) -{ - unsigned int i; - Htable *ht; - - if (keylenfn == NULL || keycmpfn == NULL || freekeyfn == NULL || freevalfn == NULL) { - printf("error: callback function(s) cannot be NULL\n"); - return NULL; - } - - /* create a hash table */ - if ((ht = calloc(1, sizeof(Htable))) == NULL) { - printf("error: calloc: %s\n", strerror(errno)); - return NULL; - } - if ((ht->nodes = calloc(cap, sizeof(Htnode))) == NULL) { - printf("error: calloc: %s\n", strerror(errno)); - return NULL; - } - ht->cap = cap; - ht->len = 0; - ht->keylenfn = keylenfn; - ht->keycmpfn = keycmpfn; - ht->freekeyfn = freekeyfn; - ht->freevalfn = freevalfn; - - for (i = 0; i < cap; i++) - ht->nodes[i] = NULL; - return ht; -} - +/* + * Destroy the hash table and, if the free_key_val flag is true, free the keys and values. + */ static void -__htdestroy(Htable *ht, int dofree) +destroy_and_free(struct htable *ht, int free_key_val) { unsigned int i; - Htnode *n; - Htnode *tmp; + struct htnode *n, *tmp; for (i = 0; i < ht->cap; i++) { for (n = ht->nodes[i]; n != NULL;) { - if (dofree) { - ht->freekeyfn(n->key); - ht->freevalfn(n->val); + if (free_key_val) { + ht->key_free(n->key); + ht->val_free(n->val); } tmp = n; n = n->next; @@ -77,160 +32,196 @@ __htdestroy(Htable *ht, int dofree) free(ht); } -void -htdestroy(Htable *ht) -{ - __htdestroy(ht, 1); -} - -void * -htsearch(Htable *ht, void *key) -{ - unsigned int i; - Htnode *n; - - i = hash(ht, key); - for (n = ht->nodes[i]; n != NULL; n = n->next) { - if (ht->keycmpfn(key, n->key) == 0) - return n->val; - } - return NULL; -} - -int -htinsert(Htable *ht, void *key, void *val) +/* + * Insert a new node or, if the replace flag is true, replace the value of an existing node. + */ +static int +insert_or_replace_val(struct htable *ht, void *key, void *val, int replace) { unsigned int i; - Htnode *n; /* current node */ - Htnode *pn; /* previous node */ + struct htnode *n, *ln; /* current and last node */ - pn = NULL; - i = hash(ht, key); + i = ht->hash(key, ht->cap); + ln = NULL; for (n = ht->nodes[i]; n != NULL; n = n->next) { - /*if key already exist, override value */ - if (ht->keycmpfn(key, n->key) == 0) { - ht->freekeyfn(n->key); - ht->freevalfn(n->val); - n->key = key; - n->val = val; - return 0; + /* if key already exist */ + if (ht->key_cmp(key, n->key) == 0) { + if (replace) { + ht->val_free(n->val); + n->val = val; + return 0; + } else { + return -1; + } } - pn = n; + ln = n; } - /* create a new node */ - if ((n = calloc(1, sizeof(Htnode))) == NULL) { - printf("error: calloc: %s\n", strerror(errno)); + if (replace) /* failed to replace */ + return -1; + + if ((n = malloc(sizeof(struct htnode))) == NULL) { + perror("error: malloc"); return -1; } n->key = key; n->val = val; n->next = NULL; - /* link to the previous node */ - if (pn == NULL) + /* link to the last node */ + if (ln == NULL) ht->nodes[i] = n; else - pn->next = n; - /* increment the hash table length */ + ln->next = n; ht->len++; - return 1; + return 0; } +/* + * Remove a node or, if the replace flag is true, change the key of the node. + */ static int -__htremove(Htable *ht, void *key, int freeval) +remove_or_replace_key(struct htable *ht, void *key, int replace, void *newkey) { unsigned int i; - Htnode *n; /* current node */ - Htnode *pn; /* previous node */ + struct htnode *n, *ln; /* current and last node */ - pn = NULL; - i = hash(ht, key); + i = ht->hash(key, ht->cap); + ln = NULL; for (n = ht->nodes[i]; n != NULL; n = n->next) { - if (ht->keycmpfn(key, n->key) == 0) { - /* free node memory */ - ht->freekeyfn(n->key); - if (freeval) - ht->freevalfn(n->val); - /* link to the previous node */ - if (pn == NULL) + if (ht->key_cmp(key, n->key) == 0) { + ht->key_free(n->key); + if (!replace) { + ht->val_free(n->val); + } else { + if (htinsert(ht, newkey, n->val) == -1) + return -1; + } + /* link to the last node */ + if (ln == NULL) ht->nodes[i] = n->next; else - pn->next = n->next; - /* free the node */ + ln->next = n->next; free(n); - return 1; + return 0; } - pn = n; + ln = n; } return -1; } +struct htable * +htcreate(hash_fn *hash, key_cmp_fn *key_cmp, key_free_fn *key_free, + val_free_fn *val_free, unsigned int cap) +{ + unsigned int i; + struct htable *ht; + + if ((ht = malloc(sizeof(struct htable))) == NULL) { + perror("error: malloc"); + return NULL; + } + if ((ht->nodes = malloc(cap * sizeof(struct htnode *))) == NULL) { + perror("error: malloc"); + return NULL; + } + for (i = 0; i < cap; i++) + ht->nodes[i] = NULL; + + ht->cap = cap; + ht->len = 0; + ht->hash = hash; + ht->key_cmp = key_cmp; + ht->key_free = key_free; + ht->val_free = val_free; + return ht; +} + +void +htdestroy(struct htable *ht) +{ + destroy_and_free(ht, 1); +} + +void * +htsearch(struct htable *ht, void *key) +{ + unsigned int i; + struct htnode *n; + + i = ht->hash(key, ht->cap); + for (n = ht->nodes[i]; n != NULL; n = n->next) { + if (ht->key_cmp(key, n->key) == 0) + return n->val; + } + return NULL; +} + int -htremove(Htable *ht, void *key) +htinsert(struct htable *ht, void *key, void *val) { - return __htremove(ht, key, 1); + return insert_or_replace_val(ht, key, val, 0); } int -htsetkey(Htable *ht, void *oldkey, void *newkey) +htremove(struct htable *ht, void *key) { - void *val = NULL; - if ((val = htsearch(ht, oldkey)) == NULL) - return -1; - __htremove(ht, oldkey, 0); - htinsert(ht, newkey, val); - return 1; + return remove_or_replace_key(ht, key, 0, NULL); } -void -htresize(Htable *ht, unsigned int ncap) +int +htmodkey(struct htable *ht, void *oldkey, void *newkey) { - unsigned int i, tmp; - Htable *nht; /* new hash table */ - Htnode *n; /* current node */ - Htnode **list; /* list of nodes */ + return remove_or_replace_key(ht, oldkey, 1, newkey); +} - /* create a new hash table */ - nht = htcreate(ht->keylenfn, ht->keycmpfn, ht->freekeyfn, ht->freevalfn, ncap); - for (i = 0; i < ht->cap; i++) { - for (n = ht->nodes[i]; n != NULL; n = n->next) - htinsert(nht, n->key, n->val); - } +int +htmodval(struct htable *ht, void *key, void *newval) +{ + return insert_or_replace_val(ht, key, newval, 1); +} + +struct htable * +htresize(struct htable *ht, unsigned int newcap) +{ + struct htable *newht; + struct htiter it; - /* swap old hash table with new one */ - list = ht->nodes; - ht->nodes = nht->nodes; - nht->nodes = list; + newht = htcreate(ht->hash, ht->key_cmp, ht->key_free, ht->val_free, newcap); + if (newht == NULL) + return NULL; - tmp = ht->cap; - ht->cap = nht->cap; - nht->cap = tmp; + htiter_init(&it); + while(htiterate(ht, &it)) { + if (htinsert(newht, it.node->key, it.node->val) == -1) { + htdestroy(newht); + return NULL; + } + } - tmp = ht->len; - ht->len = nht->len; - nht->len = tmp; + destroy_and_free(ht, 0); + return newht; +} - /* destroy new(old) hash table */ - __htdestroy(nht, 0); +void +htiter_init(struct htiter *it) +{ + it->index = 0; + it->node = NULL; } int -htiterate(Htable *ht, Htiter *it) +htiterate(struct htable *ht, struct htiter *it) { - if (it->index == 0 && it->node == NULL) { - it->index = 0; - it->node = ht->nodes[0]; - } else { + if (it->node != NULL) it->node = it->node->next; - } - while (it->index < ht->cap) { - while (it->node != NULL) { - return 1; - it->node = it->node->next; - } - it->index++; + while (it->node == NULL && it->index < ht->cap) { it->node = ht->nodes[it->index]; + it->index++; } - return 0; + + if (it->node != NULL) + return 1; + else + return 0; } diff --git a/htable.h b/htable.h @@ -3,41 +3,48 @@ * See COPYING file for more information. */ -typedef struct Htnode Htnode; -typedef struct Htable Htable; -typedef struct Htiter Htiter; +#ifndef HTABLE_H +#define HTABLE_H -typedef size_t (KeyLenFn)(const void *key); -typedef int (KeyCmpFn)(const void *key1, const void *key2); -typedef void (FreeKeyFn)(void *ptr); -typedef void (FreeValFn)(void *ptr); +typedef unsigned int (hash_fn)(void *key, unsigned int cap); +typedef int (key_cmp_fn)(const void *key1, const void *key2); +typedef void (key_free_fn)(void *key); +typedef void (val_free_fn)(void *val); -struct Htnode { - void *key; /* key for the node */ - void *val; /* value for the node */ - Htnode *next; /* next node (open hash table) */ +struct htnode { + void *key; + void *val; + struct htnode *next; }; -struct Htable { - unsigned int cap; /* capacity */ - unsigned int len; /* length */ - Htnode **nodes; /* list of nodes */ - KeyLenFn *keylenfn; /* key length function */ - KeyCmpFn *keycmpfn; /* key compare function */ - FreeKeyFn *freekeyfn; - FreeValFn *freevalfn; +struct htable { + struct htnode **nodes; + unsigned int cap; + unsigned int len; + hash_fn *hash; + key_cmp_fn *key_cmp; + key_free_fn *key_free; + val_free_fn *val_free; }; -struct Htiter { +struct htiter { unsigned int index; - Htnode *node; + struct htnode *node; }; -Htable *htcreate(KeyLenFn *keylenfn, KeyCmpFn *keycmpfn, FreeKeyFn *freekeyfn, FreeValFn *freevalfn, unsigned int cap); -void htdestroy(Htable *ht); -void *htsearch(Htable *ht, void *key); -int htinsert(Htable *ht, void *key, void *val); -int htremove(Htable *ht, void *key); -int htsetkey(Htable *ht, void *oldkey, void *newkey); -void htresize(Htable *ht, unsigned int ncap); -int htiterate(Htable *ht, Htiter *it); +struct htable *htcreate(hash_fn *hash, + key_cmp_fn *key_cmp, + key_free_fn *key_free, + val_free_fn *val_free, + unsigned int cap); +void htdestroy(struct htable *ht); +void *htsearch(struct htable *ht, void *key); +int htinsert(struct htable *ht, void *key, void *val); +int htremove(struct htable *ht, void *key); +int htmodkey(struct htable *ht, void *oldkey, void *newkey); +int htmodval(struct htable *ht, void *key, void *newval); +struct htable *htresize(struct htable *ht, unsigned int newcap); +void htiter_init(struct htiter *it); +int htiterate(struct htable *ht, struct htiter *it); + +#endif /* HTABLE_H */ diff --git a/main.c b/main.c @@ -14,107 +14,91 @@ #ifdef __gnu_linux__ #include <sys/epoll.h> -#include <bsd/err.h> -#include <bsd/stdlib.h> -#include <bsd/string.h> +char *strlcpy(char *dst, const char *src, size_t n) +{ + return strncpy(dst, src, n); +} #else #include <sys/event.h> #endif +#define LOG_LEVEL 1 + #include "htable.h" #include "util.c" #define CLONE_COOLDOWN 1 #define CLONE_ADDEND 10 -/* #define PING_TIMEOUT 240 */ -#define EVENT_ADDEND 100 +#define FD_ADDEND 100 #define NET_ADDEND 10 #define USER_ADDEND 100 #define BUFSIZE 1024 #define NICK_LEN 16 -#define MAX_EVENTS 10 +#define HANDLE_EVENTS 10 /* no. of events to handle at a time */ -#define EV_READ 1 -#define EV_WRITE 2 +#define EV_READ 1 +#define EV_WRITE 2 enum { IDLE = 0, RESET, - CLONING + CLONING, + DONE }; -struct network { - int id; /* event index */ - char *name; /* name */ - char *symb; /* symbol */ - char *host; /* host */ - char *port; /* port */ - char *chan; /* channel */ - int ready; /* joined */ +struct vuser { + char *user; /* nick */ + int netid; /* net index */ + int suffix; /* suffix count */ + int ready; /* joined */ }; -struct event { - int fd; /* event fd */ - int netid; /* net index */ - char *user; /* user nick */ - int suffix; /* suffix count */ - time_t time; /* last response */ - int ready; /* joined */ +struct network { + int fd; /* fd */ + char *name; /* name */ + char *symb; /* symbol */ + char *host; /* host */ + char *port; /* port */ + char *chan; /* channel */ }; - -static int debug; -static int done; /* program state */ -static int fifofd; /* fifo fd */ -static char *fifopath; /* fifo path */ -static char msg [BUFSIZE]; /* message buffer */ -static int timeout_id = -1; +static time_t ntime = -1; /* next timeout */ +static int state = IDLE; /* program state */ +static int fifofd; /* fifo fd */ +static int break_evloop; /* break event loop */ +static char *fifopath; /* fifo path */ +static char msg[BUFSIZE]; /* message buffer */ #ifdef __gnu_linux__ -static int epfd; /* epoll instance */ +static int epfd; /* epoll instance */ #else -static int kqfd; /* kqueue instance */ +static int kqfd; /* kqueue instance */ #endif -static int *fdtoid; /* fd to event id */ -static int fdslen; /* maximum fd */ - -static struct event *events; /* events array */ -static int evslen; /* array length */ -static int evscap; /* array capacity */ - -static struct network *networks; /* networks array */ -static int netlen; /* array length */ -static int netcap; /* array capacity */ -static int state = IDLE; - -/* - * hash table of users - * key -> <user_nick> + '[' + <network_symbol> + ']' - * value -> array of all user's clones event ids indexed - * corresponding to its connected network - */ -static struct Htable *users; +static struct network *networks; /* networks array */ +static int netlen; /* array length */ +static int netcap; /* array capacity */ +static struct vuser **fdtovuser; /* fd -> vuser pointer */ +static int fdcap; /* fdtovuser capacity */ +static struct htable *usertofds; /* user -> array of fds of clones + indexed according to networks array */ /* functions prototype */ - void fd_register(int, int); void fifo_read(void); time_t event_timeout(void); -void event_write(int); -int event_read(int); +void fd_write(int); +void fd_read(int); void net_add(char *, char *, char *, char *, char *); -void net_del(char *); +void net_del(int, char *); void net_del_raw(int); -void net_users_add(int, char *); void user_add(char *, int, int); void user_del(char *, char *); -int *user_event_ids(char *, int); -int clone_add(char *, int); -int event_add(int, int, char *); -void event_del(int); +int vuser_add(int, char *); +void vuser_del(int, char *); +int *vuser_get_fds(char *, int); void nick_add_symb(char *, int); void privmsg_update(char *, char *, int); void print_table(void); @@ -126,177 +110,167 @@ int main(int argc, char *argv[]) { #ifdef __gnu_linux__ - struct epoll_event epevs [MAX_EVENTS]; /* maximum event to handle */ + struct epoll_event epevs[HANDLE_EVENTS]; #else - struct kevent kevs [MAX_EVENTS]; /* maximum event to handle */ - struct timespec tmsp; + struct kevent kevs[HANDLE_EVENTS]; + struct timespec tmsp; #endif - int i, r, fd, id, nev; + int i, fd, *fds, nev; /* no. of returned events to handle */ time_t timeout; - time_t ntime; /* next timeout time */ + struct htiter it; /* set stdout to unbufferd */ setvbuf(stdout, NULL, _IONBF, 0); - /* check arguments */ - if (argc != 2) { - printf("usage: %s fifo\n", getprogname()); - return 0; - } else { + /* handle arguments */ + if (argc != 2) + fatal("usage: ticl fifo"); + else fifopath = argv[1]; - } /* init global variables */ - fdslen = evscap = EVENT_ADDEND; netcap = NET_ADDEND; - fdtoid = ecalloc((size_t)fdslen, sizeof(int)); - events = ecalloc((size_t)evscap, sizeof(struct event)); - networks = ecalloc((size_t)netcap, sizeof(struct network)); - users = htcreate((KeyLenFn *)strlen, (KeyCmpFn *)strcmp, free, free, - USER_ADDEND); + fdcap = FD_ADDEND; + networks = emalloc((size_t)netcap * sizeof(struct network)); + fdtovuser = emalloc((size_t)fdcap * sizeof(struct vuser *)); + usertofds = htcreate(hash_str, (key_cmp_fn *)strcmp, free, free, + USER_ADDEND); #ifdef __gnu_linux__ if ((epfd = epoll_create1(0)) == -1) - err(1, "epoll_create1"); + fatal("epoll_create1:"); #else if ((kqfd = kqueue()) == -1) - err(1, "kqueue"); + fatal("kqueue:"); #endif fifofd = fifo_open(fifopath); fd_register(fifofd, EV_READ); - ntime = -1; /* event loop */ - while (!done) { - if (ntime == -1) + while (state != DONE) { + if (ntime == -1) { timeout = -1; - else if ((timeout = ntime - time(NULL)) < 0) - timeout = 0; + } else { + timeout = ntime - time(NULL); + if (timeout < 0) + timeout = 0; + } #ifdef __gnu_linux__ - if ((nev = epoll_wait(epfd, epevs, MAX_EVENTS, - (int)timeout * 1000)) == -1) - err(1, "epoll_wait"); + nev = epoll_wait(epfd, epevs, HANDLE_EVENTS, (int)timeout * 1000); + if (nev == -1) + fatal("epoll_wait:"); #else tmsp.tv_sec = timeout; tmsp.tv_nsec = 0; - if ((nev = kevent(kqfd, NULL, 0, kevs, MAX_EVENTS, - timeout < 0 ? NULL : &tmsp)) == -1) - err(1, "kevent"); + nev = kevent(kqfd, NULL, 0, kevs, HANDLE_EVENTS, timeout < 0 ? NULL : &tmsp); + if (nev == -1) + fatal("kevent:"); #endif - if (nev == 0) { + else if (nev == 0) { ntime = event_timeout(); continue; } - if (debug) { - printf("fds:"); - for (i = 0; i < nev; i++) { - printf(" %d", epevs[i].data.fd); - } - printf("\n"); - } - for (i = 0; i < nev; i++) { #ifdef __gnu_linux__ fd = epevs[i].data.fd; #else fd = (int)kevs[i].ident; #endif - id = fdtoid[fd]; if (fd == fifofd) { fifo_read(); + break; #ifdef __gnu_linux__ } else if (epevs[i].events & EPOLLOUT) { #else } else if (kevs[i].filter == EVFILT_WRITE) { #endif - event_write(id); + fd_write(fd); #ifdef __gnu_linux__ } else if (epevs[i].events & EPOLLIN) { #else } else if (kevs[i].filter == EVFILT_READ) { #endif - r = event_read(id); - if (r == 1) { - timeout_id = -1; - ntime = 0; - } else if (r == -2) { + fd_read(fd); + if (break_evloop) { + break_evloop = FALSE; break; } } else { - errx(1, "unknown event"); + fatal("unknown event"); } } } - /* - * cleanup - */ - snprintf(msg, sizeof(msg), "QUIT :linker shutting down\r\n"); - for (i = 0; i < evslen; i++) { - writeall(events[i].fd, msg); - close(events[i].fd); + /* cleanup */ + snprintf(msg, sizeof(msg), "QUIT :relay shutting down\r\n"); + + /* delete and free all the users with their fds */ + htiter_init(&it); + while(htiterate(usertofds, &it)) { + fds = (int *)it.node->val; + for (i = 0; i < netlen; i++) { + if (fds[i] > 0) + vuser_del(fds[i], msg); + } } + htdestroy(usertofds); - free(fdtoid); - free(events); - - /* delete all the networks */ - for (i = 0; i < netlen; i++) + /* delete and free all the networks with their fd */ + for (i = 0; i < netlen; i++) { + vuser_del(networks[i].fd, msg); net_del_raw(i); + } free(networks); - /* delete all the users */ - htdestroy(users); - + free(fdtovuser); return 0; } void -fd_register(int fd, int type) +fd_register(int fd, int mode) { #ifdef __gnu_linux__ struct epoll_event ev; - if (type == EV_READ) + if (mode == EV_READ) ev.events = EPOLLIN; - else if (type == EV_WRITE) + else if (mode == EV_WRITE) ev.events = EPOLLOUT; else - errx(1, "unkown event type"); + fatal("unknown event mode"); ev.data.fd = fd; if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) == -1) - err(1, "epoll_ctl"); + fatal("epoll_ctl:"); #else struct kevent ev; int filter; - if (type == EV_READ) + if (mode == EV_READ) filter = EVFILT_READ; - else if (type == EV_WRITE) + else if (mode == EV_WRITE) filter = EVFILT_WRITE; else - errx(1, "unkown event type"); + fatal("unknown event mode"); EV_SET(&ev, fd, filter, EV_ADD, 0, 0, 0); if (kevent(kqfd, &ev, 1, NULL, 0, NULL) == -1) - err(1, "kevent"); + fatal("kevent:"); #endif - /* printf("added fd: %d\n", fd); */ } void fifo_read(void) { - char buffer [BUFSIZE]; + char buffer[BUFSIZE]; char *buf; char *cmd; ssize_t n; n = readline(fifofd, buffer, sizeof(buffer)); if (n == -1) { - err(1, "fifo: read"); + fatal("read:"); } else if (n == 0) { /* reopen fifo again */ close(fifofd); @@ -304,8 +278,7 @@ fifo_read(void) fd_register(fifofd, EV_READ); return; } - - if (!*buffer) + if (*buffer == '\0') return; buf = buffer; @@ -317,15 +290,24 @@ fifo_read(void) char *port = split(&buf, ' '); char *chan = buf; if (!*name || !*symb || !*host || !*port || !*chan) - printf("usage: netadd <name> <symbol> <host> <port> <channel>\n"); + warnf("usage: netadd <name> <symbol> <host> <port> <channel>"); else net_add(name, symb, host, port, chan); } else if (strcmp(cmd, "netdel") == 0) { char *name = buf; - if (!*name) - printf("usage: netdel <name>\n"); - else - net_del(name); + if (!*name) { + warnf("usage: netdel <name>"); + } else { + int i; + for (i = 0; i < netlen; i++) { + if (strcmp(name, networks[i].name) == 0) { + snprintf(msg, sizeof(msg), "QUIT :unlinking %s\r\n", networks[i].name); + net_del(i, msg); + return; + } + } + warnf("%s: network doesn't exist", name); + } } else if (strcmp(cmd, "print") == 0) { print_table(); } else if (strcmp(cmd, "htable") == 0) { @@ -333,104 +315,73 @@ fifo_read(void) } else if (strcmp(cmd, "users") == 0) { print_users(); } else if (strcmp(cmd, "exit") == 0) { - done = TRUE; + state = DONE; } else { - warnx("%s is not a command", cmd); + warnf("%s is not a command", cmd); } } time_t event_timeout(void) { - static Htiter it = {0}; + static struct htiter it; - int i, j, *ids; - char *nick; - struct network *n; - time_t ntime; - - /* if (timeout_id != -1) { - printf("%d: PING\n", events[timeout_id].fd); - snprintf(msg, sizeof(msg), "PING %s\r\n", - networks[events[timeout_id].netid].host); - writeall(events[timeout_id].fd, msg); - events[timeout_id].time = time(NULL); - } else { */ - if (state == RESET) { - state = CLONING; - it.index = 0; - it.node = NULL; - } - printf("Adding %d bot\n", CLONE_ADDEND); + int i, j, *fds; + char *user; - j = 0; - while (htiterate(users, &it)) { - nick = (char *)it.node->key; - ids = (int *)it.node->val; - for (i = 0; i < netlen; i++) { - n = &networks[i]; - if (!n->ready || ids[i] != 0) - continue; - ids[i] = clone_add(nick, i); - } - if (++j >= CLONE_ADDEND) - goto calculate; - } - state = IDLE; - it.index = 0; - it.node = NULL; - /* } */ - -calculate: - ntime = -1; - timeout_id = -1; - - if (state == CLONING) - ntime = time(NULL) + CLONE_COOLDOWN; - - /* for(i = 0; i < evslen; i++) { - if ((events[i].time != 0) - && ((ntime == -1) || (events[i].time + PING_TIMEOUT < ntime))) { - ntime = events[i].time + PING_TIMEOUT; - timeout_id = i; + if (state == RESET) { + state = CLONING; + htiter_init(&it); + } + log1("."); + + j = 0; + while (htiterate(usertofds, &it)) { + user = (char *)it.node->key; + fds = (int *)it.node->val; + for (i = 0; i < netlen; i++) { + if (fds[i] == 0 && fdtovuser[networks[i].fd]->ready) + fds[i] = vuser_add(i, user); } - } */ - return ntime; + j++; + if (j >= CLONE_ADDEND) + return time(NULL) + CLONE_COOLDOWN; + } + state = IDLE; + return -1; } void -event_write(int id) +fd_write(int fd) { - int fd = events[id].fd; #ifdef __gnu_linux__ struct epoll_event ev; ev.events = EPOLLIN; ev.data.fd = fd; if (epoll_ctl(epfd, EPOLL_CTL_MOD, fd, &ev) == -1) - err(1, "epoll_ctl"); + fatal("epoll_ctl:"); #else struct kevent ev; EV_SET(&ev, fd, EV_WRITE, EV_DISABLE, 0, 0, 0); if (kevent(kqfd, &ev, 1, NULL, 0, NULL) == -1) - err(1, "kevent"); + fatal("kevent:"); fd_register(fd, EV_READ); #endif - if (events[id].user == NULL) { /* linker */ + if (fdtovuser[fd]->user == NULL) { /* linker */ snprintf(msg, sizeof(msg), "NICK linker\r\n"); writeall(fd, msg); snprintf(msg, sizeof(msg), "USER linker 0 * :linker\r\n"); writeall(fd, msg); } else { /* user */ - snprintf(msg, sizeof(msg), "NICK %s\r\n", events[id].user); + snprintf(msg, sizeof(msg), "NICK %s\r\n", fdtovuser[fd]->user); writeall(fd, msg); snprintf(msg, sizeof(msg), "USER user 0 * :user\r\n"); writeall(fd, msg); } - events[id].time = time(NULL); } -int -event_read(int id) +void +fd_read(int fd) { char buffer [BUFSIZE]; char backup [BUFSIZE]; @@ -438,47 +389,43 @@ event_read(int id) char *buf; char *cmd; char *nick; - int i, fd, netid; + int i, netid; ssize_t n; - fd = events[id].fd; - netid = events[id].netid; - events[id].time = time(NULL); + netid = fdtovuser[fd]->netid; n = readline(fd, buffer, sizeof(buffer)); if (n == -1) { - warn("%d: read", fd); - event_del(id); - return 0; + warnf("%d: read:", fd); + snprintf(msg, sizeof(msg), "QUIT :read failed\r\n"); + goto del; } else if (n == 0) { - warnx("%d: connection closed", fd); - event_del(id); - return 0; + warnf("%d: connection closed", fd); + snprintf(msg, sizeof(msg), "QUIT :connection closed\r\n"); + goto del; } - - if (!*buffer) - return 0; + if (*buffer == '\0') + return; /* clone the buffer */ - strlcpy(backup, buffer, sizeof(buffer)); + strlcpy(backup, buffer, sizeof(backup)); buf = buffer; /* set linker nick */ strlcpy(lnick, "linker", sizeof(lnick)); - for (i = 0; i < events[id].suffix; i++) + for (i = 0; i < fdtovuser[fd]->suffix; i++) strcat(lnick, "_"); /* first column */ cmd = split(&buf, ' '); - if (strcmp(cmd, "NOTICE") == 0) { - return 0; + if (strcmp(cmd, "NOTICE") == 0) { /* ignore */ + return; } else if (strcmp(cmd, "ERROR") == 0) { - event_del(id); - goto printbuffer; + fatal("%d: %s", fd, backup); } else if (strcmp(cmd, "PING") == 0) { snprintf(msg, sizeof(msg), "PONG %s\r\n", buf); writeall(fd, msg); - return 0; + return; } /* strip nick from first column */ nick = split(&cmd, '!'); @@ -489,70 +436,65 @@ event_read(int id) cmd = split(&buf, ' '); /* ignore all the info messages */ - if ((strcmp(cmd, "002") == 0) - || (strcmp(cmd, "003") == 0) - || (strcmp(cmd, "004") == 0) - || (strcmp(cmd, "005") == 0) - || (strcmp(cmd, "003") == 0) - || (strcmp(cmd, "251") == 0) - || (strcmp(cmd, "252") == 0) - || (strcmp(cmd, "253") == 0) /* unknown connection(s) */ - || (strcmp(cmd, "254") == 0) - || (strcmp(cmd, "255") == 0) - || (strcmp(cmd, "265") == 0) - || (strcmp(cmd, "266") == 0) - || (strcmp(cmd, "250") == 0) - || (strcmp(cmd, "332") == 0) - || (strcmp(cmd, "333") == 0) - || (strcmp(cmd, "375") == 0) - || (strcmp(cmd, "372") == 0) - || (strcmp(cmd, "376") == 0) - || (strcmp(cmd, "396") == 0) - || (strcmp(cmd, "366") == 0) - || (strcmp(cmd, "MODE") == 0) - || (strcmp(cmd, "TOPIC") == 0) - || (strcmp(cmd, "NOTICE") == 0)) { - return 0; + if ((strcmp(cmd, "002") == 0) || + (strcmp(cmd, "003") == 0) || + (strcmp(cmd, "004") == 0) || + (strcmp(cmd, "005") == 0) || + (strcmp(cmd, "003") == 0) || + (strcmp(cmd, "251") == 0) || + (strcmp(cmd, "252") == 0) || + (strcmp(cmd, "253") == 0) || /* unknown connection(s) */ + (strcmp(cmd, "254") == 0) || + (strcmp(cmd, "255") == 0) || + (strcmp(cmd, "265") == 0) || + (strcmp(cmd, "266") == 0) || + (strcmp(cmd, "250") == 0) || + (strcmp(cmd, "332") == 0) || + (strcmp(cmd, "333") == 0) || + (strcmp(cmd, "375") == 0) || + (strcmp(cmd, "372") == 0) || + (strcmp(cmd, "376") == 0) || + (strcmp(cmd, "396") == 0) || + (strcmp(cmd, "366") == 0) || + (strcmp(cmd, "MODE") == 0) || + (strcmp(cmd, "TOPIC") == 0) || + (strcmp(cmd, "NOTICE") == 0)) { + return; } else if (strcmp(cmd, "432") == 0) { /* Erroneous Nickname */ - event_del(id); - goto printbuffer; + fatal("%d: %s", fd, backup); } else if (strcmp(cmd, "433") == 0) { /* Nickname already in use */ split(&buf, ' '); nick = split(&buf, ' '); strcat(nick, "_"); - events[id].suffix++; + fdtovuser[fd]->suffix++; if (strlen(nick) > NICK_LEN) { - warnx("nick '%s' is too big", nick); - if (strcmp(nick, lnick) == 0) { - net_del(networks[netid].name); - } else { - snprintf(msg, sizeof(msg), "QUIT :nick is too big\r\n"); - user_del(nick, msg); - } + warnf("%s: nickname too long", nick); + snprintf(msg, sizeof(msg), "QUIT :%s: nickname too long\r\n", nick); + goto del; } else { snprintf(msg, sizeof(msg), "NICK %s\r\n", nick); writeall(fd, msg); } - return 0; + return; } else if (strcmp(cmd, "001") == 0) { snprintf(msg, sizeof(msg), "JOIN %s\r\n", networks[netid].chan); writeall(fd, msg); - return 0; + return; } else if (strcmp(cmd, "PRIVMSG") == 0) { - char privmsg [BUFSIZE] = ""; - int *ids; + char privmsg[BUFSIZE] = ""; + int *fds; - if (events[id].user == NULL) { /* if linker */ + if (fdtovuser[fd]->user == NULL) { /* if linker */ nick_add_symb(nick, netid); - if ((ids = htsearch(users, nick)) == NULL) - return 0; + if ((fds = htsearch(usertofds, nick)) == NULL) + return; split(&buf, ':'); /* set buf to msg */ privmsg_update(privmsg, buf, netid); for (i = 0; i < netlen; i++) { - if (ids[i] > 0) { + if (fds[i] > 0) { snprintf(msg, sizeof(msg), "PRIVMSG %s :%s\r\n", networks[i].chan, privmsg); - writeall(events[ids[i]].fd, msg); + writeall(fds[i], msg); } } } else { @@ -560,12 +502,12 @@ event_read(int id) char *user = split(&buf, ' '); /* ignore messages from channel (it is handled by linker) */ - if ((user[0] == '#') || (user[0] == '&')) - return 0; + if (user[0] == '#' || user[0] == '&') + return; nick_add_symb(nick, netid); - if ((ids = htsearch(users, nick)) == NULL) - return 0; + if ((fds = htsearch(usertofds, nick)) == NULL) + return; /* split user nick and network symbol */ *strrchr(user, ']') = '\0'; @@ -581,237 +523,221 @@ event_read(int id) split(&buf, ':'); /* set buf to msg */ privmsg_update(privmsg, buf, netid); snprintf(msg, sizeof(msg), "PRIVMSG %s :%s\r\n", user, privmsg); - writeall(events[ids[i]].fd, msg); + writeall(fds[i], msg); } - return 0; + return; + } + else if (strcmp(cmd, "353") == 0) { + fdtovuser[fd]->ready = TRUE; + /* FALLBACK */ } /* these messages are handled by linker */ - if (events[id].user != NULL) { /* if clone */ - if (strcmp(cmd, "353") == 0) { - events[id].ready = TRUE; - return 0; - } else if ((strcmp(cmd, "JOIN") == 0) - || (strcmp(cmd, "QUIT") == 0) - || (strcmp(cmd, "PART") == 0) - || (strcmp(cmd, "KICK") == 0) - || (strcmp(cmd, "NICK") == 0)) { - return 0; + if (fdtovuser[fd]->user != NULL) { /* if clone */ + if ((strcmp(cmd, "353") == 0) || + (strcmp(cmd, "JOIN") == 0) || + (strcmp(cmd, "QUIT") == 0) || + (strcmp(cmd, "PART") == 0) || + (strcmp(cmd, "KICK") == 0) || + (strcmp(cmd, "NICK") == 0)) { + return; } } else if (strcmp(cmd, "353") == 0) { char *nick; split(&buf, ':'); - networks[netid].ready = TRUE; state = RESET; + ntime = 0; /* then add all new users */ while (*(nick = split(&buf, ' ')) != '\0') { - if (*nick == '@' - || *nick == '&' - || *nick == '~' - || *nick == '%' - || *nick == '+' - || *nick == '\\') + if (*nick == '@' || + *nick == '&' || + *nick == '~' || + *nick == '%' || + *nick == '+' || + *nick == '\\') nick++; if (strcmp(nick, lnick) != 0) user_add(nick, netid, FALSE); } - return 1; + return; } else if (strcmp(cmd, "JOIN") == 0) { - if ((strcmp(nick, lnick) != 0) - && (user_event_ids(nick, netid) == NULL)) { /* if not clone */ + /* if real user */ + if ((strcmp(nick, lnick) != 0) && + (vuser_get_fds(nick, netid) == NULL)) { if (state != IDLE) - warnx("ignoring user '%s' due to network cloning", nick); + warnf("%s: ignored (network cloning)", nick); else user_add(nick, netid, TRUE); } - return 0; - } else if ((strcmp(cmd, "QUIT") == 0) - || (strcmp(cmd, "PART") == 0)) { + return; + } else if ((strcmp(cmd, "QUIT") == 0) || (strcmp(cmd, "PART") == 0)) { + split(&buf, ':'); /* ignore ':' and assign QUIT/PART msg to buf */ snprintf(msg, sizeof(msg), "QUIT :%s\r\n", buf); nick_add_symb(nick, netid); - if (htsearch(users, nick) != NULL) + if (htsearch(usertofds, nick) != NULL) { user_del(nick, msg); - return -2; + break_evloop = TRUE; + return; + } + return; } else if (strcmp(cmd, "NICK") == 0) { - int *ids, i; + int *fds, i; char *newnick; nick_add_symb(nick, netid); - if ((ids = htsearch(users, nick)) == NULL) - return 0; + if ((fds = htsearch(usertofds, nick)) == NULL) + return; /* set buf to new nick */ split(&buf, ':'); - /* allocate a newnick and append the netsym and then replace the old */ - newnick = ecalloc(strlen(buf) + strlen(networks[netid].symb) + 2 + 1, sizeof(char)); + /* allocate a new nick with net symbol and replace the old one */ + newnick = emalloc((strlen(buf) + strlen(networks[netid].symb) + 2 + 1) * sizeof(char)); sprintf(newnick, "%s[%s]", buf, networks[netid].symb); - htsetkey(users, nick, newnick); - + htmodkey(usertofds, nick, newnick); snprintf(msg, sizeof(msg), "NICK %s\r\n", newnick); for (i = 0; i < netlen; i++) { - events[ids[i]].user = newnick; - if (ids[i] > 0) - writeall(events[ids[i]].fd, msg); + if (fds[i] > 0) { + fdtovuser[fds[i]]->user = newnick; + writeall(fds[i], msg); + } } - return 0; + return; } else if (strcmp(cmd, "KICK") == 0) { - /* :<nick_which_is_kicking>!~user@host KICK <channel> <nick_which_has_been_kicked> :<kick_msg> */ - int *ids; - char *chan = split(&buf, ' '); - char *user = split(&buf, ' '); + /* :<user_who_kicked>!~username@host KICK <channel> <user_who_is_being_kicked> :<kick_msg> */ + int *fds; + char *user; - /* set the quit msg */ - snprintf(msg, sizeof(msg), "QUIT : kicked by %s\r\n", nick); + split(&buf, ' '); /* channel name */ + user = split(&buf, ' '); /* user who is being kicked */ + split(&buf, ':'); /* ignore ':' and store reason to buf */ - /* delete whole network if it is the linker */ + /* if linker is being kicked, delete the network */ if (strcmp(user, lnick) == 0) { - net_del(networks[netid].name); - return 0; + snprintf(msg, sizeof(msg), "QUIT :netdel: %s (%s is kicked by %s)\r\n", + networks[netid].name, lnick, nick); + net_del(netid, msg); + break_evloop = TRUE; + return; } - /* delete the user if the message from the same network */ - if ((ids = user_event_ids(user, netid)) == NULL) { + /* if message is from a real user, delete the user */ + if ((fds = vuser_get_fds(user, netid)) == NULL) { + snprintf(msg, sizeof(msg), "QUIT :userdel: %s (kicked by %s)\r\n", user, nick); nick_add_symb(user, netid); user_del(user, msg); - return 0; + break_evloop = TRUE; + return; } - /* close the kicked fd */ - writeall(fd, msg); - event_del(id); + /* delete the kicked clone */ + snprintf(msg, sizeof(msg), "QUIT :kicked by %s\r\n", nick); + vuser_del(fds[netid], msg); - /* - * send notice in the channel through linker - */ /* get the original user netid */ for (i = 0; i < netlen; i++) { - if (ids[i] == -1) + if (fds[i] == -1) break; } - /* set buf to msg */ - split(&buf, ':'); - /* remove netsymb and suffix */ - *strrchr(user, '[') = '\0'; - /* send notice */ - snprintf(msg, sizeof(msg), - "PRIVMSG %s :%s was kicked out by %s from network %s %s [%s]\r\n", - networks[i].chan, user, nick, networks[netid].name, chan, buf); - writeall(events[networks[i].id].fd, msg); - return 0; + /* send notice in the channel through linker */ + snprintf(msg, sizeof(msg), "PRIVMSG %s :%s is kicked by %s [%s]\r\n", + networks[i].chan, user, nick, buf); + writeall(networks[i].fd, msg); + break_evloop = TRUE; + return; } -printbuffer: - printf("%d: %s\n", fd, backup); - return 0; + warnf("%d: %s", fd, backup); + return; +del: + if (fdtovuser[fd]->user == NULL) + net_del(netid, msg); + else + vuser_del(fd, msg); + break_evloop = TRUE; + return; } void net_add(char *name, char *symb, char *host, char *port, char *chan) { struct network *n; - int i, fd; + int i; - /* if name, symbol or configuration already exists */ for (i = 0; i < netlen; i++) { if (strcmp(networks[i].name, name) == 0) { - warnx("network name '%s' already exists", name); + warnf("%s: network name already exists", name); return; } if (strcmp(networks[i].symb, symb) == 0) { - warnx("network symbol '%s' already exists", symb); + warnf("%s: network symbol already exists", symb); return; } - if ((strcmp(networks[i].host, host) == 0) - && (strcmp(networks[i].port, port) == 0) - && (strcmp(networks[i].chan, chan) == 0)) { - warnx("network configuration already exists"); + if ((strcmp(networks[i].host, host) == 0) && + (strcmp(networks[i].port, port) == 0) && + (strcmp(networks[i].chan, chan) == 0)) { + warnf("%s:%s%s: network configuration already exists", + host, port, chan); return; } } - /* resize if full */ + /* if full, resize the network and user fds */ if (netlen == netcap) { - Htiter it = {0}; - networks = realloc0(networks, sizeof(struct network) * (size_t)netcap, - sizeof(struct network) * (size_t)(netcap + NET_ADDEND)); - while (htiterate(users, &it)) - it.node->val = realloc0(it.node->val, sizeof(int) * (size_t)netcap, - sizeof(int) * (size_t)(netcap + NET_ADDEND)); + struct htiter it; + htiter_init(&it); + + networks = erealloc(networks, (size_t)(netcap + NET_ADDEND) * + sizeof(struct network)); + while (htiterate(usertofds, &it)) { + it.node->val = erealloc(it.node->val, + (size_t)(netcap + NET_ADDEND) * + sizeof(int)); + /* zero out the extended array */ + for (i = netcap; i < netcap + NET_ADDEND; i++) + ((int *)it.node->val)[i] = 0; + } netcap += NET_ADDEND; } - /* connect */ - if ((fd = dial(host, port)) == -1) - return; /* add a network */ n = &networks[netlen]; - n->id = event_add(fd, netlen, NULL); n->name = strdup(name); n->symb = strdup(symb); n->host = strdup(host); n->port = strdup(port); n->chan = strdup(chan); - n->ready = FALSE; + n->fd = vuser_add(netlen, NULL); netlen++; - - printf("%d: network '%s' added\n", fd, name); } void -net_del(char *name) +net_del(int netid, char *msg) { - int i, netid, *ids; - - Htiter it = {0}; /* current iterator */ - Htiter lastit = {0}; /* last iterator */ - - /* get netid */ - netid = -1; - for (i = 0; i < netlen; i++) { - if (strcmp(name, networks[i].name) == 0) { - netid = i; - break; - } - } - if (netid == -1) { - warnx("network '%s' doesn't exist", name); - return; - } - - /* set the quit msg */ - snprintf(msg, sizeof(msg), "QUIT :unlinking network %s\r\n", name); - - /* reconstruct the user-clones table */ - while (htiterate(users, &it)) { - ids = (int *)it.node->val; - /* delete all the users of deleting network */ - if (ids[netid] == -1) { + int *fds; + struct htiter lit, it; /* last, current iterator */ + + htiter_init(&it); + htiter_init(&lit); + while (htiterate(usertofds, &it)) { + fds = (int *)it.node->val; + if (fds[netid] == -1) { user_del(it.node->key, msg); - /* this node is deleted */ - it = lastit; - /* delete the clones */ + it = lit; /* this node is deleted */ } else { - if (ids[netid] > 0) { - writeall(events[ids[netid]].fd, msg); - event_del(ids[netid]); - } - /* swap last with current one */ - ids[netid] = ids[netlen-1]; + if (fds[netid] > 0) + vuser_del(fds[netid], msg); + fds[netid] = fds[netlen-1]; + if (fds[netid] > 0) + fdtovuser[fds[netid]]->netid = netid; + fds[netlen-1] = 0; } - lastit = it; - } - - /* set last netid of events to current netid. */ - for (i = 0; i < evslen; i++) { - if (events[i].netid == netlen-1) - events[i].netid = netid; + lit = it; } - writeall(events[networks[netid].id].fd, msg); - event_del(networks[netid].id); + vuser_del(networks[netid].fd, msg); net_del_raw(netid); - printf("%d: network '%s' deleted\n", events[networks[netid].id].fd, name); - /* swap the network with the last */ networks[netid] = networks[netlen-1]; + if (networks[netid].fd > 0) + fdtovuser[networks[netid].fd]->netid = netid; netlen--; } @@ -831,161 +757,132 @@ user_add(char *unick, int netid, int clone) { size_t len; char *nick; - int *ids, i; + int *fds; len = strlen(unick) + strlen(networks[netid].symb) + 2 + 1; - - /* too long nick */ if (len-1 > NICK_LEN) { - warnx("nick '%s' is too big", unick); + warnf("%s[%s]: nickname too long", unick, networks[netid].symb); return; } /* resize hash table if storage is low */ - if ((users->cap - users->len) < USER_ADDEND) - htresize(users, users->cap + USER_ADDEND); + if ((usertofds->cap - usertofds->len) < USER_ADDEND) + usertofds = htresize(usertofds, usertofds->cap + USER_ADDEND); /* allocate a new user */ - nick = ecalloc(len, sizeof(char)); - ids = ecalloc((size_t)netcap, sizeof(int)); + nick = emalloc(len * sizeof(char)); + fds = ecalloc((size_t)netcap, sizeof(int)); sprintf(nick, "%s[%s]", unick, networks[netid].symb); - ids[netid] = -1; - - if (debug) - printf("useradd: %s\n", nick); + fds[netid] = -1; if (clone) { - /* clone the user on all other network */ + int i; for (i = 0; i < netlen; i++) { - if (networks[i].ready && i != netid) - ids[i] = clone_add(nick, i); + if (fds[i] == 0 && fdtovuser[networks[i].fd]->ready) + fds[i] = vuser_add(i, nick); } } - /* insert it to the users hash table */ - if (htinsert(users, nick, ids) == -1) - /* this shouldn't happen as it was already checked */ - errx(1, "user '%s' already exists", nick); + if (htinsert(usertofds, nick, fds) == -1) + fatal("%s: user already exists", nick); } void user_del(char *nick, char *msg) { - int i, *ids; + int i, *fds; - if ((ids = htsearch(users, nick)) == NULL) - errx(1, "user '%s' 1 doesn't exists", nick); + if ((fds = htsearch(usertofds, nick)) == NULL) + fatal("%s: user doesn't exist", nick); for (i = 0; i < netlen; i++) { - if (ids[i] <= 0) - continue; - printf("%d: del[%s]: %s\n", events[ids[i]].fd, - networks[i].symb, nick); - - if (events[ids[i]].ready) - writeall(events[ids[i]].fd, msg); - event_del(ids[i]); + if (fds[i] > 0) + vuser_del(fds[i], msg); } - htremove(users, nick); -} - -int * -user_event_ids(char *nick, int netid) -{ - unsigned int s; - int *ids = NULL; - - /* count suffix */ - for (s = 0; nick[strlen(nick)-s-1] == '_'; s++); - /* remove suffix */ - if (s > 0) - nick[strlen(nick)-s] = '\0'; - - ids = htsearch(users, nick); - /* if match but suffix doesn't match */ - if ((ids != NULL) && (events[ids[netid]].suffix != (int)s)) - ids = NULL; - - /* add suffix back */ - if (s > 0) - nick[strlen(nick)] = '_'; - - return ids; + htremove(usertofds, nick); } int -clone_add(char *nick, int netid) +vuser_add(int netid, char *user) { - struct network *n; - int fd; + int fd; + struct network *n; + struct vuser *vuser; n = &networks[netid]; - if ((fd = dial(n->host, n->port)) == -1) { - warn("enable to connect to %s\n", n->host); - return -1; + fd = dial(n->host, n->port); + if (fd == -1) { + warnf("%s:%s: failed to connect", n->host, n->port); + return -2; } - printf("%d: add[%s]: %s\n", fd, n->symb, nick); - return event_add(fd, netid, nick); -} + fd_register(fd, EV_WRITE); -int -event_add(int fd, int netid, char *user) -{ - int i = evslen; + if (user == NULL) + log1("%d: netadd: %s[%s]", fd, n->name, n->symb); + else + log1("%d: add[%s]: %s", fd, n->symb, user); - if (evslen == evscap) { - events = realloc0(events, sizeof(struct event) * (size_t)evscap, - sizeof(struct event) * (size_t)(evscap + EVENT_ADDEND)); - evscap += EVENT_ADDEND; + if (fd+1 > fdcap) { + fdcap *= 2; + fdtovuser = erealloc(fdtovuser, (size_t)fdcap * sizeof(struct vuser *)); } - events[i].fd = fd; - events[i].netid = netid; - events[i].user = user; - events[i].suffix = 0; - events[i].time = 0; - events[i].ready = FALSE; + vuser = emalloc(1 * sizeof(struct vuser)); + vuser->netid = netid; + vuser->user = user; + vuser->suffix = 0; + vuser->ready = FALSE; - fd_register(fd, EV_WRITE); + fdtovuser[fd] = vuser; - if (fd == fdslen - 1) { - fdtoid = realloc0(fdtoid, sizeof(int) * (size_t)fdslen, - sizeof(int) * (size_t)fdslen * 2); - fdslen *= 2; - } - fdtoid[fd] = i; - - return evslen++; + return fd; } void -event_del(int id) +vuser_del(int fd, char *msg) { - int *ids; - int l = evslen - 1; /* last id */ - - /* swap id */ - if (events[l].user == NULL) { /* if linker */ - networks[events[l].netid].id = id; - } else { /* else user */ - if ((ids = htsearch(users, events[l].user)) == NULL) - errx(1, "user '%s' 2 doesn't exists", events[l].user); - ids[events[l].netid] = id; - } + char *user = fdtovuser[fd]->user; + int netid = fdtovuser[fd]->netid; + int *fds; - /* disable id */ - if (events[id].user == NULL) { /* if linker */ - networks[events[id].netid].id = -2; - } else { /* else user */ - if ((ids = htsearch(users, events[id].user)) == NULL) - errx(1, "user '%s' 3 doesn't exists", events[id].user); - ids[events[id].netid] = -2; + if (user == NULL) { + log1("%d: netdel: %s[%s]", fd, networks[netid].name, networks[netid].symb); + networks[netid].fd = -2; + } else { + log1("%d: del[%s]: %s", fd, networks[netid].symb, user); + if ((fds = htsearch(usertofds, user)) == NULL) + fatal("%s: user doesn't exist", user); + fds[netid] = -2; } - close(events[id].fd); + if (fdtovuser[fd]->ready) + writeall(fd, msg); + close(fd); + free(fdtovuser[fd]); + fdtovuser[fd] = NULL; +} + +int * +vuser_get_fds(char *nick, int netid) +{ + unsigned int s; + int *fds = NULL; + + /* count suffix */ + for (s = 0; nick[strlen(nick)-s-1] == '_'; s++); + /* remove suffix */ + if (s > 0) + nick[strlen(nick)-s] = '\0'; - events[id] = events[l]; - evslen--; - fdtoid[events[id].fd] = id; + fds = htsearch(usertofds, nick); + /* if match but suffix doesn't match */ + if ((fds != NULL) && (fdtovuser[fds[netid]]->suffix != (int)s)) + fds = NULL; + + /* add suffix back */ + if (s > 0) + nick[strlen(nick)] = '_'; + + return fds; } void @@ -1017,7 +914,7 @@ privmsg_update(char *dst, char *src, int netid) } /* check if the word is nick */ - if (user_event_ids(src, netid) != NULL) + if (vuser_get_fds(src, netid) != NULL) *strrchr(src, '[') = '\0'; strcat(dst, src); @@ -1030,35 +927,42 @@ privmsg_update(char *dst, char *src, int netid) void print_table(void) { - int i, *ids, diff, tabs; - Htiter it = {0}; + int i, *fds, diff, tabs; char *nick; + struct htiter it; if (netlen == 0) return; print_border(); /* print networks */ - printf("struct networks\t\t"); - for (i = 0; i < netlen; i++) - printf("%s(%d)\t", networks[i].symb, events[networks[i].id].fd); + printf("Networks\t\t"); + for (i = 0; i < netlen; i++) { + printf("%s->%d", networks[i].symb, networks[i].fd); + /* print suffix */ + if (fdtovuser[networks[i].fd]->suffix > 0) + printf("(%d)\t", fdtovuser[networks[i].fd]->suffix); + else + printf("\t\t"); + } printf("\n"); - while (htiterate(users, &it)) { - ids = (int *)it.node->val; + htiter_init(&it); + while (htiterate(usertofds, &it)) { + fds = (int *)it.node->val; nick = (char *)it.node->key; /* print tabbed user nick */ printf("%s", nick); diff = 24 - (int)strlen(nick); tabs = ((diff / 8) + (diff % 8 > 0)); printf("%.*s", tabs, "\t\t\t"); - /* print tabbed clones ids */ + /* print tabbed fds */ for (i = 0; i < netlen; i++) { - printf("%d", ids[i]); + printf("%d", fds[i]); /* print suffix */ - if ((ids[i] > 0) && (events[ids[i]].suffix > 0)) - printf("(%d)", events[ids[i]].suffix); - printf("\t"); + if ((fds[i] > 0) && (fdtovuser[fds[i]]->suffix > 0)) + printf("(%d)", fdtovuser[fds[i]]->suffix); + printf("\t\t"); } printf("\n"); } @@ -1068,16 +972,17 @@ print_table(void) void print_htable(void) { - Htiter it = {0}; - int index = -1; + struct htiter it; + int index = -1; print_border(); - while (htiterate(users, &it)) { + htiter_init(&it); + while (htiterate(usertofds, &it)) { if (index != (int)it.index) { /* ignore first new line */ if (index != -1) printf("\n"); - printf("%d", it.index); + printf("%d", it.index-1); index = (int)it.index; } printf(" -> %s", (char *)it.node->key); @@ -1089,11 +994,12 @@ print_htable(void) void print_users(void) { - Htiter it = {0}; + struct htiter it; int i = 0; print_border(); - while (htiterate(users, &it)) + htiter_init(&it); + while (htiterate(usertofds, &it)) printf("%d: %s\n", i++, (char *)it.node->key); print_border(); } diff --git a/util.c b/util.c @@ -9,45 +9,103 @@ #include <errno.h> #include <fcntl.h> #include <netdb.h> +#include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> -#ifdef __gnu_linux__ -#include <bsd/err.h> -#else -#include <err.h> -#endif +#define FALSE 0 +#define TRUE 1 -#define FALSE 0 -#define TRUE 1 +void +print_log(int level, FILE *stream, const char *fmt, va_list arg) +{ + size_t len; + + if (level > LOG_LEVEL) + return; + + vfprintf(stream, fmt, arg); + len = strlen(fmt); + if (len && fmt[len-1] == ':') + fprintf(stream, " %s\n", strerror(errno)); + else + fprintf(stream, "\n"); +} + +void +log1(const char *fmt, ...) +{ + va_list arg; + va_start(arg, fmt); + print_log(1, stdout, fmt, arg); + va_end(arg); +} + +void +log2(const char *fmt, ...) +{ + va_list arg; + va_start(arg, fmt); + print_log(2, stdout, fmt, arg); + va_end(arg); +} + +void +log3(const char *fmt, ...) +{ + va_list arg; + va_start(arg, fmt); + print_log(3, stdout, fmt, arg); + va_end(arg); +} + +void +warnf(const char *fmt, ...) +{ + va_list arg; + fprintf(stderr, "warn: "); + va_start(arg, fmt); + print_log(0, stderr, fmt, arg); + va_end(arg); +} + +void +fatal(const char *fmt, ...) +{ + va_list arg; + fprintf(stderr, "error: "); + va_start(arg, fmt); + print_log(0, stderr, fmt, arg); + va_end(arg); + exit(1); +} + +void * +emalloc(size_t size) +{ + void *p; + if ((p = malloc(size)) == NULL) + fatal("malloc:"); + return p; +} -/* - * ecalloc - calloc with error handling - */ void * ecalloc(size_t nmemb, size_t size) { void *p; if ((p = calloc(nmemb, size)) == NULL) - err(1, "calloc"); + fatal("calloc:"); return p; } -/* - * realloc0 -- allocate more memory and zero new memory - * ptr - pointer of the old memory - * osize - size of old memory - * nsize - size to new memory - */ void * -realloc0(void *ptr, size_t osize, size_t nsize) +erealloc(void *ptr, size_t size) { void *p; - if ((p = realloc(ptr, nsize)) == NULL) - err(1, "realloc"); - memset(((char *)p + osize), 0, nsize - osize); + if ((p = realloc(ptr, size)) == NULL) + fatal("realloc:"); return p; } @@ -70,42 +128,6 @@ split(char **str, char ch) } int -fifo_open(char *path) -{ - struct stat st; - int fd; - - /* make fifo if it doesn't exists */ - if (lstat(path, &st) != -1) { - if (!(st.st_mode & S_IFIFO)) - errx(1, "'%s' is not a fifo file", path); - } else if (mkfifo(path, S_IRWXU) == -1) { - err(1, "mkfifo: %s", path); - } - - fd = open(path, O_RDONLY | O_NONBLOCK, 0); - if (fd == -1) - err(1, "open: %s", path); - - return fd; -} - -ssize_t -writeall(int fd, char *buf) -{ - ssize_t left, sent, n; - left = (ssize_t)strlen(buf); - sent = 0; - while (sent < left) { - if ((n = write(fd, buf+sent, (size_t)left)) == -1) - err(1, "%d: write", fd); - sent += n; - left -= n; - } - return sent; -} - -int dial(char *host, char *port) { static struct addrinfo hints, *res = NULL, *res0; @@ -116,32 +138,54 @@ dial(char *host, char *port) hints.ai_socktype = SOCK_STREAM; if ((r = getaddrinfo(host, port, &hints, &res0)) != 0) { - warnx("getaddrinfo: %s", gai_strerror(r)); + warnf("getaddrinfo: %s", gai_strerror(r)); return -1; } for (res = res0; res != NULL; res = res->ai_next) { - if ((fd = socket(res->ai_family, res->ai_socktype, - res->ai_protocol)) == -1) + fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol); + if (fd == -1) { + warnf("socket:"); continue; + } if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { - warn("fnctl"); + warnf("fnctl:"); continue; } - if ((connect(fd, res->ai_addr, res->ai_addrlen) == -1) - && (errno == EINPROGRESS)) + if ((connect(fd, res->ai_addr, res->ai_addrlen) == -1) && + (errno == EINPROGRESS)) break; - - warn("connect"); + warnf("connect:"); close(fd); fd = -1; } if (fd == -1) - warnx("cannot connect to %s", host); + warnf("failed to connect to %s", host); freeaddrinfo(res0); return fd; } +int +fifo_open(char *path) +{ + struct stat st; + int fd; + + /* make a fifo file if it doesn't exists */ + if (lstat(path, &st) != -1) { + if (!(st.st_mode & S_IFIFO)) + fatal("%s: not a fifo file", path); + } else if (mkfifo(path, S_IRWXU) == -1) { + fatal("mkfifo: %s:", path); + } + + fd = open(path, O_RDONLY | O_NONBLOCK, 0); + if (fd == -1) + fatal("open: %s:", path); + + return fd; +} + ssize_t readline(int fd, char *buffer, size_t size) { @@ -149,18 +193,44 @@ readline(int fd, char *buffer, size_t size) char c; ssize_t l; - do { + while (i < size) { l = read(fd, &c, sizeof(char)); - if (l > 0) - buffer[i++] = c; - else if (l == 0) - return 0; - else if (l == -1 && errno == EAGAIN) + if (l == -1 && errno == EAGAIN) continue; - else - return -1; - } while ((i < size) && (c != '\r') && (c != '\n')); + else if (l == -1 || l == 0) + return l; - buffer[i-1] = '\0'; + if (c == '\r' || c == '\n') + break; + buffer[i++] = c; + } + buffer[i++] = '\0'; return (ssize_t)i; } + +ssize_t +writeall(int fd, char *buf) +{ + ssize_t left, sent, n; + left = (ssize_t)strlen(buf); + sent = 0; + while (sent < left) { + if ((n = write(fd, buf+sent, (size_t)left)) == -1) + fatal("%d: write:", fd); + sent += n; + left -= n; + } + return sent; +} + +/* very simple string hash function */ +unsigned int +hash_str(void *key, unsigned int cap) +{ + unsigned int i, sum = 0; + + for (i = 0; i < (unsigned int)strlen(key); i++) { + sum += ((unsigned char *)key)[i] * (i + 1); + } + return (sum % cap); +}