ticl

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

commit 49ca82832b3505a7ce86e96f11a90599219afaab
parent 8cdec92600494e5d844cb79e25d95215cda12f8a
Author: libredev <libredev@ircforever.org>
Date:   Sun, 15 Jan 2023 16:37:08 +0530

replaced picoev with kqueue and epoll

Diffstat:
M.gitmodules | 3---
MMakefile | 50++++++++++++++++++--------------------------------
MREADME | 239+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Aconfig.mk | 17+++++++++++++++++
Mmain.c | 780++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Dpicoev | 1-
Mutil.c | 122++++++++++++++++++++++++++++++++++++-------------------------------------------
7 files changed, 709 insertions(+), 503 deletions(-)

diff --git a/.gitmodules b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "picoev"] - path = picoev - url = https://github.com/kazuho/picoev diff --git a/Makefile b/Makefile @@ -1,51 +1,37 @@ # This work is dedicated to the public domain. # See COPYING file for more information. -PREFIX = /usr/local +include config.mk -CC = cc -CFLAGS = -g -std=c89 -Wall -Wextra -pedantic -Wfatal-errors -Wconversion\ - -Wstrict-prototypes -Wold-style-definition\ - -D_POSIX_C_SOURCE=200809L -isystem ./picoev/ -#CFLAGS += -fsanitize=address -fno-omit-frame-pointer -#LDFLAGS = -fsanitize=address -LDFLAGS = -L ./picoev/ -l picoev +OUT = ticl +SRC = main.c htable.c +OBJ = $(SRC:.c=.o) -VERSION != date '+%Y-%m-%d' -PROGRAM = ticl -SOURCES = main.c htable.c -HEADERS = htable.h util.c -OBJECTS = $(SOURCES:.c=.o) - -all: clean libpicoev.a $(PROGRAM) - -libpicoev.a: - cd picoev && $(MAKE) libpicoev.a LINUX_BUILD=1 CC_DEBUG_FLAGS=-g - -$(PROGRAM): $(OBJECTS) - $(CC) -o $@ $(OBJECTS) $(LDFLAGS) +all: clean $(OUT) .c.o: $(CC) -c $(CFLAGS) $< +$(OUT): $(OBJ) + $(CC) -o $@ $(OBJ) $(LDFLAGS) + clean: - rm -f $(PROGRAM) $(OBJECTS) $(PROGRAM)-$(VERSION).tar.gz - cd picoev && $(MAKE) clean + rm -f $(OUT) $(OBJ) $(OUT)-*.tar.gz dist: clean - mkdir -p $(PROGRAM)-$(VERSION) - cp -R README COPYING Makefile $(SOURCES) $(HEADERS)\ - $(PROGRAM)-$(VERSION) - tar -cf $(PROGRAM)-$(VERSION).tar $(PROGRAM)-$(VERSION) - gzip $(PROGRAM)-$(VERSION).tar - rm -rf $(PROGRAM)-$(VERSION) + mkdir -p $(OUT)-$(VERSION) + cp -R README COPYING Makefile config.mk htable.h util.c $(SRC)\ + $(OUT)-$(VERSION) + tar -cf $(OUT)-$(VERSION).tar $(OUT)-$(VERSION) + gzip $(OUT)-$(VERSION).tar + rm -rf $(OUT)-$(VERSION) install: all mkdir -p $(DESTDIR)$(PREFIX)/bin - cp -f $(PROGRAM) $(DESTDIR)$(PREFIX)/bin - chmod 755 $(DESTDIR)$(PREFIX)/bin/$(PROGRAM) + cp -f $(OUT) $(DESTDIR)$(PREFIX)/bin + chmod 755 $(DESTDIR)$(PREFIX)/bin/$(OUT) uninstall: - rm -f $(DESTDIR)$(PREFIX)/bin/$(PROGRAM) + rm -f $(DESTDIR)$(PREFIX)/bin/$(OUT) .PHONY: all clean dist install uninstall diff --git a/README b/README @@ -1,174 +1,221 @@ -==================================================================== +====================================================================== WARNING: This program is still in progress, use it at your own risk. -==================================================================== +====================================================================== ticl - tiny irc channel linker ------------------------------ ticl is a very small and simple multi-network irc channel linker. +WARNING: If you use this program on networks that you are not allowed + then your nick or IP may get banned. This is because this + program opens multiple simultaneous connections to the server + and most networks have some limit on each IP. + Working ------- -A bot named 'linker' joins each given channels and clones each user of its -channel on other channels and relays the user's messages received from its -channel to the clones on other channels. +- A bot named 'linker' joins each given channels and clones each user + of its channel on other channels and relays the user's messages + received from its channel to the clones on other channels. + +- If a user on a channel joins(JOIN), quits(QUIT), leaves(PART), or + changes their nick(NICK), the linker attempts to emulate the same + action with the respective clones on the other channels. -If a user JOIN, QUIT, PART, or NICK, the linker attempts to emulate the -same action with the clones on the other channels. +- Each clone's nick is followed by the original user's channel symbol + (enclosed in square brackets). -The nick of clones has the network symbol (included in square brackets) as -a suffix. +- If the nick is already taken by another user/clone on that network, + an additional suffix '_' is added until the nick is accepted on that + network. -If nick is already taken then additional suffix '_' will be added until -nick is accepted on the network. +- Nick of users or clones that are longer than 16 characters are + ignored as some networks only allow nicknames of length 16 or less. -Limitation ----------- +Limitations +----------- -- Networks and channels that require any type of registration or verification - will not work because clones cannot register themselves. +- This program will not work on any channel that requires any kind of + registration or verification as clones cannot register. -- Linking any two channels that are already linked will create an infinite - loop of clones and may get your IP banned. +- Linking the same channel with a different name is undefined and can + create an infinite loop of clones. -- Channels on the same network will not link (can be possible in the future). +- Linking more than one channel from a network is undefined. - No spam protection. +- No support for TLS/SSL. + Features -------- -- written in POSIX ANSI C. -- one clone for every user on all other channels. -- no support for TLS/SSL (this is a feature). +- Written in OpenBSD's C style(9). + +- One clone per user on each channel. Dependencies ------------ - C compiler (C89) -- libc (with POSIX support) + +- C POSIX library + +- libbsd (on non-BSD operating systems) + - POSIX make (optional) -Compiling ---------- +Installation +------------- - $ make -or - $ gcc ticl.c htable.c utils.c -o ticl +Edit config.mk for your system. +Then to compile and install, run: -Example -------- + $ make clean install + +To compile without POSIX make on BSD systems, run: + + $ cc -o ticl main.c htable.c + +Or on non-BSD systems, run: + + $ cc -o ticl main.c htable.c -lbsd + + +Usages +------ -To start the program with 'in' as the fifo file: +This program uses FIFO special file (a named pipe) for configuration. - $ ./ticl ./in +To start the program: + + $ ticl <fifo> + + # Example: + + $ ticl in + + # This will create a 'in' FIFO file if it doesn't already exist. Or, to start the program with the log printing to a file: - $ ./ticl ./in > ticl.log + $ ticl <fifo> > <logfile> 2>&1 -To link channel #test20 from libera and channel #test21 from ircnow: + # Example: - $ echo 'netadd libera L irc.libera.chat 6667 #test20' > ./in - $ echo 'netadd ircnow N irc.ircnow.org 6667 #test21' > ./in + $ ticl in > log 2>&1 -To unlink channel ircnow: + # This will create a 'log' file and print everything to the file. - $ echo 'netdel ircnow' > ./in +Or, to start and run the program in background: -To list all the sockets (connections): + $ ticl <fifo> > <logfile> 2>&1 & - $ echo 'print' > ./in + # Example: - # -1 indicate original user - # -2 indicate kicked clone + $ ticl in > log 2>&1 & -To list all the users: +To add a channel: + + $ echo 'netadd <name> <symbol> <host> <ip> <channel>' > <fifo> + + # Example, to link #test20 from libera and #test21 from ircnow: - $ echo 'users' > ./in + $ echo 'netadd libera L irc.libera.chat 6667 #test20' > in + $ echo 'netadd ircnow N irc.ircnow.org 6667 #test21' > in -To shutdown the program: +To remove a channel from the link: - $ echo exit > ./in + $ echo 'netdel <name>' > <fifo> + # Example, to unlink channel ircnow: -Community ---------- + $ echo 'netdel ircnow' > in -Join #playground on irc.ircnow.org:6697 (TLS) +To list all the users: -For any help, tag or private message me (libredev). + $ echo users > <fifo> -NOTE: Anything is allowed as long as you are not violating IRCNow TOS. - (https://wiki.ircnow.org/index.php?n=Terms.Terms) +To close the program: -Email: libredev@ircforever.org (expect late response) + $ echo exit > <fifo> -SSL/TLS support +SSL/TLS Support --------------- -1. relayd (OpenBSD) -------------------- +On Openbsd (relayd): + + Edit /etc/relayd.conf: + + table <libera> { irc.libera.chat } + table <ircnow> { irc.ircnow.org } + + protocol "irctls" { + tcp { nodelay, sack } + } + + relay "libera" { + listen on 127.0.0.1 port 31220 + protocol "irctls" + forward with tls to <libera> port 6697 + } + + relay "ircnow" { + listen on 127.0.0.1 port 31221 + protocol "irctls" + forward with tls to <ircnow> port 6697 + } + + To enable and start: -/etc/relayd.conf: + $ doas rcctl enable relayd + $ doas rcctl start relayd - table <libera> { irc.libera.chat } - table <ircnow> { irc.ircnow.org } +On other platforms (stunnel): - protocol "irctls" { - tcp { nodelay, sack } - } + Edit /etc/stunnel/stunnel.conf: - relay "libera" { - listen on 127.0.0.1 port 31220 - protocol "irctls" - forward with tls to <libera> port 6697 - } + pid = /etc/stunnel/pid - relay "ircnow" { - listen on 127.0.0.1 port 31221 - protocol "irctls" - forward with tls to <ircnow> port 6697 - } + [libera] + client = yes + accept = 127.0.0.1:31220 + connect = irc.libera.chat:6697 + checkHost = irc.libera.chat + verifyChain = yes + CApath = /etc/ssl/certs + OCSPaia = yes -2. stunnel (*BSD, GNU/Linux, GNU/Hurd, Plan9, etc) --------------------------------------------------- + [ircnow] + client = yes + accept = 127.0.0.1:31221 + connect = irc.ircnow.org:6697 + checkHost = irc.ircnow.org + verifyChain = yes + CApath = /etc/ssl/certs + OCSPaia = yes -On debian, /etc/stunnel/stunnel.conf: + Then enable and start stunnel service. - pid = /etc/stunnel/pid +Now to connect: - [libera] - client = yes - accept = 127.0.0.1:31220 - connect = irc.libera.chat:6697 - checkHost = irc.libera.chat - verifyChain = yes - CApath = /etc/ssl/certs - OCSPaia = yes + $ echo 'netadd libera L 127.0.0.1 31220 #test20' > in + $ echo 'netadd ircnow N 127.0.0.1 31221 #test21' > in - [ircnow] - client = yes - accept = 127.0.0.1:31221 - connect = irc.ircnow.org:6697 - checkHost = irc.ircnow.org - verifyChain = yes - CApath = /etc/ssl/certs - OCSPaia = yes -3. To connect: --------------- +Community / Bug Report +---------------------- - $ echo 'netadd libera L 127.0.0.1 31220 #test20' > ./in - $ echo 'netadd ircnow N 127.0.0.1 31221 #test21' > ./in +Email: libredev@ircforever.org (expect late response) +IRC: #playground on irc.ircnow.org:6697 (TLS) License @@ -181,6 +228,6 @@ See COPYING file for more information. Note ---- -It is a free software and please do not call it open source as the +This work is free software but please don't call it open source as the Open Source Initiative (OSI) does not consider public domain software as open source. https://opensource.org/node/878 diff --git a/config.mk b/config.mk @@ -0,0 +1,17 @@ +# This work is dedicated to the public domain. +# See COPYING file for more information. + +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) +LDFLAGS = $(LIBS) + +CC = cc diff --git a/main.c b/main.c @@ -4,40 +4,57 @@ */ #include <errno.h> +#include <execinfo.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <poll.h> +#include <signal.h> #include <time.h> #include <unistd.h> -#include <picoev.h> +#ifdef __gnu_linux__ +#include <sys/epoll.h> +#include <bsd/err.h> +#include <bsd/stdlib.h> +#include <bsd/string.h> +#else +#include <sys/event.h> +#endif #include "htable.h" #include "util.c" -#define BUFFER_LEN 1024 -#define NICK_LEN 16 +#define FALSE 0 +#define TRUE 1 + +#define EV_READ 1 +#define EV_WRITE 2 -#define EVENT_ADDEND 10000 +#define EVENT_ADDEND 100 #define NET_ADDEND 10 #define USER_ADDEND 100 -#define EVENT_TIMEOUT 10000 +#define PING_TIMEOUT 240 +#define CLONE_COOLDOWN 1 +#define CLONE_ADDEND 10 -#define MAX_FDS 10000 -#define CLONE_COOLDOWN 5 -#define CLONE_ADDEND 5 +#define BUFSIZE 1024 +#define NICK_LEN 16 +#define MAX_EVENTS 10 -#define UNUSED(x) (void)(x) +enum { + IDLE = 0, + RESET, + CLONING +}; struct network { int id; /* event index */ - int join; /* is joined */ char *name; /* name */ char *symb; /* symbol */ - char *host; /* hostname */ + char *host; /* host */ char *port; /* port */ char *chan; /* channel */ + int ready; /* joined */ }; struct event { @@ -45,18 +62,35 @@ struct event { int netid; /* net index */ char *user; /* user nick */ int suffix; /* suffix count */ + time_t time; /* last response */ + int ready; /* joined */ }; -static char msg [BUFFER_LEN]; -static int done; -static struct event *events; /* events array */ -static int evlen; /* array length */ -static int evcap; /* array capacity */ +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; + +#ifdef __gnu_linux__ +static int epfd; /* epoll instance */ +#else +static int kqfd; /* kqueue instance */ +#endif -static struct network *networks; /* networks array */ -static int netlen; /* array length */ -static int netcap; /* array capacity */ +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 @@ -67,22 +101,24 @@ static int netcap; /* array capacity */ static struct Htable *users; /* functions prototype */ -void fifo_event_cb(picoev_loop *, int, int, void *); -void server_event_cb(picoev_loop *, int, int, void *); -void net_add(picoev_loop *, char *, char *, char *, char *, char *); -void net_del(picoev_loop *, char *); + +void fd_register(int, int); +void fifo_read(void); +time_t event_timeout(void); +void event_write(int); +int event_read(int); +void net_add(char *, char *, char *, char *, char *); +void net_del(char *); void net_del_raw(int); -void net_update(picoev_loop *, int); -void user_add(picoev_loop *, char *, int); -void user_del(picoev_loop *, char *, char *); -int *user_get_ids(char *); -int *clone_get_user_ids(char *, int); -int clone_add(picoev_loop *, char *, int); -int event_add(picoev_loop *, int, int, char *); -void event_del(picoev_loop *, 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); void nick_add_symb(char *, int); void privmsg_update(char *, char *, int); -void clean_exit(picoev_loop *, int); void print_table(void); void print_htable(void); void print_users(void); @@ -91,68 +127,180 @@ void print_border(void); int main(int argc, char *argv[]) { - picoev_loop *loop; - /* int fd; */ +#ifdef __gnu_linux__ + struct epoll_event epevs [MAX_EVENTS]; /* maximum event to handle */ +#else + struct kevent kevs [MAX_EVENTS]; /* maximum event to handle */ + struct timespec tmsp; +#endif + int i, fd, id, nev; + time_t timeout; + time_t ntime; /* next timeout time */ /* set stdout to unbufferd */ setvbuf(stdout, NULL, _IONBF, 0); /* check arguments */ - /* if (argc != 2) { - printf("usage: %s <fifo>\n", argv[0]); - return 1; - }*/ + if (argc != 2) { + printf("usage: %s fifo\n", getprogname()); + return 0; + } else { + fifopath = argv[1]; + } /* init global variables */ - evcap = EVENT_ADDEND; + fdslen = evscap = EVENT_ADDEND; netcap = NET_ADDEND; - events = ecalloc((size_t)evcap, sizeof(struct event)); + 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); - /* init picoev */ - picoev_init(MAX_FDS); - /* create loop */ - loop = picoev_create_loop(60); - /* get fifo fd */ - /* fd = fifo_open(argv[1]); */ - /* add fifo fd */ - /* picoev_add(loop, fd, PICOEV_READ, 0, fifo_event_cb, argv[1]); */ +#ifdef __gnu_linux__ + if ((epfd = epoll_create1(0)) == -1) + err(1, "epoll_create1"); +#else + if ((kqfd = kqueue()) == -1) + err(1, "kqueue"); +#endif + fifofd = fifo_open(fifopath); + fd_register(fifofd, EV_READ); + + ntime = -1; + /* event loop */ + while (!done) { + if (ntime == -1) + timeout = -1; + else if ((timeout = ntime - time(NULL)) < 0) + timeout = 0; +#ifdef __gnu_linux__ + if ((nev = epoll_wait(epfd, epevs, MAX_EVENTS, + (int)timeout * 1000)) == -1) + err(1, "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"); +#endif + 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(); +#ifdef __gnu_linux__ + } else if (epevs[i].events & EPOLLOUT) { +#else + } else if (kevs[i].filter == EVFILT_WRITE) { +#endif + event_write(id); +#ifdef __gnu_linux__ + } else if (epevs[i].events & EPOLLIN) { +#else + } else if (kevs[i].filter == EVFILT_READ) { +#endif + if (event_read(id)) { + timeout_id = -1; + ntime = 0; + } + } else { + errx(1, "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); + } + + free(fdtoid); + free(events); - net_add(loop, "libre", "L", "38.87.162.53", "6667", "#debian2"); - net_add(loop, "ircnow", "N", "irc.ircnow.org", "6667", "#debian"); + /* delete all the networks */ + for (i = 0; i < netlen; i++) + net_del_raw(i); + free(networks); - /* loop */ - while (!done) - picoev_loop_once(loop, 10); - /* clean and exit */ - clean_exit(loop, 0); + /* delete all the users */ + htdestroy(users); return 0; } void -fifo_event_cb(picoev_loop *loop, int fd, int revents, void *cb_arg) +fd_register(int fd, int type) +{ +#ifdef __gnu_linux__ + struct epoll_event ev; + + if (type == EV_READ) + ev.events = EPOLLIN; + else if (type == EV_WRITE) + ev.events = EPOLLOUT; + else + errx(1, "unkown event type"); + + ev.data.fd = fd; + if (epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev) == -1) + err(1, "epoll_ctl"); +#else + struct kevent ev; + int filter; + + if (type == EV_READ) + filter = EVFILT_READ; + else if (type == EV_WRITE) + filter = EVFILT_WRITE; + else + errx(1, "unkown event type"); + + EV_SET(&ev, fd, filter, EV_ADD, 0, 0, 0); + if (kevent(kqfd, &ev, 1, NULL, 0, NULL) == -1) + err(1, "kevent"); +#endif + /* printf("added fd: %d\n", fd); */ +} + +void +fifo_read(void) { - char buffer[BUFFER_LEN]; + char buffer [BUFSIZE]; char *buf; char *cmd; ssize_t n; - UNUSED(revents); - - n = readline(fd, buffer, sizeof(buffer)); + n = readline(fifofd, buffer, sizeof(buffer)); if (n == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK) - return; - printf("error: %d: read: %s\n", fd, strerror(errno)); - clean_exit(loop, 1); - } else if (n == 0) { /* reopen fifo again */ - picoev_del(loop, fd); - close(fd); - fd = fifo_open((char *)cb_arg); - picoev_add(loop, fd, PICOEV_READ, 0, fifo_event_cb, cb_arg); + err(1, "fifo: read"); + } else if (n == 0) { + /* reopen fifo again */ + close(fifofd); + fifofd = fifo_open(fifopath); + fd_register(fifofd, EV_READ); return; } @@ -165,15 +313,15 @@ fifo_event_cb(picoev_loop *loop, int fd, int revents, void *cb_arg) char *port = split(&buf, ' '); char *chan = buf; if (!*name || !*symb || !*host || !*port || !*chan) - printf("usage: netadd <name> <symbol> <hostname> <port> <channel>\n"); + printf("usage: netadd <name> <symbol> <host> <port> <channel>\n"); else - net_add(loop, name, symb, host, port, chan); + 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(loop, name); + net_del(name); } else if (strcmp(cmd, "print") == 0) { print_table(); } else if (strcmp(cmd, "htable") == 0) { @@ -181,103 +329,152 @@ fifo_event_cb(picoev_loop *loop, int fd, int revents, void *cb_arg) } else if (strcmp(cmd, "users") == 0) { print_users(); } else if (strcmp(cmd, "exit") == 0) { - done = 1; + done = TRUE; } else { - printf("error: %s is not a command\n", cmd); + warnx("%s is not a command", cmd); } } -void -server_event_cb(picoev_loop *loop, int fd, int revents, void *cb_arg) +time_t +event_timeout(void) { - char buffer [BUFFER_LEN]; - char backup [BUFFER_LEN]; - char lnick [NICK_LEN]; /* linker nick */ - char *buf; - char *cmd; - char *nick; - int i, id, netid; - ssize_t n; + static Htiter it = {0}; - id = (int)((struct event *)cb_arg - events); - netid = events[id].netid; - - if ((revents & PICOEV_TIMEOUT) != 0) { - int *ids, cfd, count = 0; - Htiter it = {0}; + 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); - printf("info: time to clone %d users\n", CLONE_ADDEND); + j = 0; while (htiterate(users, &it)) { + nick = (char *)it.node->key; ids = (int *)it.node->val; - buf = (char *)it.node->key; - if (ids[netid] == 0) { - cfd = clone_add(loop, buf, netid); - ids[netid] = cfd; - printf("%d. %d: %s\n", count, cfd, buf); - count++; - if (count >= CLONE_ADDEND) { - picoev_set_timeout(loop, fd, CLONE_COOLDOWN); - return; - } + for (i = 0; i < netlen; i++) { + n = &networks[i]; + if (!n->ready || ids[i] != 0) + continue; + ids[i] = clone_add(nick, i); + printf("%d: %s\n", events[ids[i]].fd, nick); } + if (++j >= CLONE_ADDEND) + goto calculate; } - picoev_set_timeout(loop, fd, 0); - return; - } - - if ((revents & PICOEV_WRITE) != 0) { - if (events[id].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); - writeall(fd, msg); - snprintf(msg, sizeof(msg), "USER user 0 * :user\r\n"); - writeall(fd, msg); + 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; } - picoev_set_events(loop, fd, PICOEV_READ); + } */ + return ntime; +} + +void +event_write(int id) +{ + 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"); +#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"); + fd_register(fd, EV_READ); +#endif + if (events[id].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); + 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) +{ + char buffer [BUFSIZE]; + char backup [BUFSIZE]; + char lnick [NICK_LEN]; /* linker nick */ + char *buf; + char *cmd; + char *nick; + int i, fd, netid; + ssize_t n; + + fd = events[id].fd; + netid = events[id].netid; + events[id].time = time(NULL); n = readline(fd, buffer, sizeof(buffer)); if (n == -1) { - if (errno == EAGAIN || errno == EWOULDBLOCK) - return; - printf("error: read: %d: %s\n", fd, strerror(errno)); - event_del(loop, id); - } else if (n == 0) { /* reopen fifo again */ - printf("error: closed: %d\n", fd); - event_del(loop, id); + warn("%d: read", fd); + event_del(id); + return 0; + } else if (n == 0) { + warnx("%d: connection closed", fd); + event_del(id); + return 0; } - /* remove CRLFs */ - for (i = 0; i < (int)strlen(buffer); i++) { - if (buffer[i] == '\r' || buffer[i] == '\n') { - buffer[i] = '\0'; - break; - } - } + if (!*buffer) + return 0; /* clone the buffer */ - strcpy(backup, buffer); + strlcpy(backup, buffer, sizeof(buffer)); buf = buffer; /* set linker nick */ - strcpy(lnick, "linker"); + strlcpy(lnick, "linker", sizeof(lnick)); for (i = 0; i < events[id].suffix; i++) strcat(lnick, "_"); /* first column */ cmd = split(&buf, ' '); if (strcmp(cmd, "NOTICE") == 0) { - return; + return 0; } else if (strcmp(cmd, "ERROR") == 0) { goto printbuffer; } else if (strcmp(cmd, "PING") == 0) { snprintf(msg, sizeof(msg), "PONG %s\r\n", buf); writeall(fd, msg); - return; + goto printbuffer; } /* strip nick from first column */ nick = split(&cmd, '!'); @@ -301,6 +498,8 @@ server_event_cb(picoev_loop *loop, int fd, int revents, void *cb_arg) || (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) @@ -308,17 +507,17 @@ server_event_cb(picoev_loop *loop, int fd, int revents, void *cb_arg) || (strcmp(cmd, "366") == 0) || (strcmp(cmd, "MODE") == 0) || (strcmp(cmd, "NOTICE") == 0)) { - return; + return 0; } else if (strcmp(cmd, "433") == 0) { /* Nickname already in use */ split(&buf, ' '); nick = split(&buf, ' '); if (strlen(nick)+1 > NICK_LEN) { - printf("error: cannot append suffix, nick '%s' is too big\n", nick); + warnx("nick '%s' is too big", nick); if (strcmp(nick, lnick) == 0) { - net_del(loop, networks[netid].name); + net_del(networks[netid].name); } else { snprintf(msg, sizeof(msg), "QUIT :nick is too big\r\n"); - user_del(loop, nick, msg); + user_del(nick, msg); } } else { strcat(nick, "_"); @@ -326,19 +525,19 @@ server_event_cb(picoev_loop *loop, int fd, int revents, void *cb_arg) snprintf(msg, sizeof(msg), "NICK %s\r\n", nick); writeall(fd, msg); } - return; + return 0; } else if (strcmp(cmd, "001") == 0) { snprintf(msg, sizeof(msg), "JOIN %s\r\n", networks[netid].chan); writeall(fd, msg); - return; + return 0; } else if (strcmp(cmd, "PRIVMSG") == 0) { - char privmsg[BUFFER_LEN] = ""; + char privmsg [BUFSIZE] = ""; int *ids; if (events[id].user == NULL) { /* if linker */ nick_add_symb(nick, netid); if ((ids = htsearch(users, nick)) == NULL) - return; + return 0; split(&buf, ':'); /* set buf to msg */ privmsg_update(privmsg, buf, netid); @@ -354,11 +553,11 @@ server_event_cb(picoev_loop *loop, int fd, int revents, void *cb_arg) /* ignore messages from channel (it is handled by linker) */ if ((user[0] == '#') || (user[0] == '&')) - return; + return 0; nick_add_symb(nick, netid); if ((ids = htsearch(users, nick)) == NULL) - return; + return 0; /* split user nick and network symbol */ *strrchr(user, ']') = '\0'; @@ -376,22 +575,27 @@ server_event_cb(picoev_loop *loop, int fd, int revents, void *cb_arg) snprintf(msg, sizeof(msg), "PRIVMSG %s :%s\r\n", user, privmsg); writeall(events[ids[i]].fd, msg); } - return; + return 0; } /* these messages are handled by linker */ if (events[id].user != NULL) { /* if clone */ - if ((strcmp(cmd, "353") == 0) - || (strcmp(cmd, "JOIN") == 0) + 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; + || (strcmp(cmd, "NICK") == 0)) { + return 0; + } } else if (strcmp(cmd, "353") == 0) { char *nick; split(&buf, ':'); - networks[netid].join = 1; - /* net_update(loop, netid); */ + networks[netid].ready = TRUE; + state = RESET; + + /* then add all new users */ while (*(nick = split(&buf, ' ')) != '\0') { if (*nick == '@' || *nick == '&' @@ -401,33 +605,34 @@ server_event_cb(picoev_loop *loop, int fd, int revents, void *cb_arg) || *nick == '\\') nick++; if (strcmp(nick, lnick) != 0) - user_add(loop, nick, netid); + user_add(nick, netid, FALSE); } - for (i = 0; i < netlen; i++) - picoev_set_timeout(loop, events[networks[i].id].fd, CLONE_COOLDOWN); - return; + return 1; } else if (strcmp(cmd, "JOIN") == 0) { if ((strcmp(nick, lnick) != 0) - && (clone_get_user_ids(nick, netid) == NULL)) { /* if not clone */ - user_add(loop, nick, netid); - for (i = 0; i < netlen; i++) - picoev_set_timeout(loop, events[networks[i].id].fd, CLONE_COOLDOWN); + && (user_event_ids(nick, netid) == NULL)) { /* if not clone */ + if (state != IDLE) + warn("ignoring user '%s' due to network cloning", nick); + else + user_add(nick, netid, TRUE); } - return; + return 0; } else if ((strcmp(cmd, "QUIT") == 0) || (strcmp(cmd, "PART") == 0)) { snprintf(msg, sizeof(msg), "QUIT :%s\r\n", buf); nick_add_symb(nick, netid); - if (htsearch(users, nick) != NULL) - user_del(loop, nick, msg); - return; + if (htsearch(users, nick) != NULL) { + printf("userdel: %s\n", nick); + user_del(nick, msg); + } + return 0; } else if (strcmp(cmd, "NICK") == 0) { int *ids, i; char *newnick; nick_add_symb(nick, netid); if ((ids = htsearch(users, nick)) == NULL) - return; + return 0; /* set buf to new nick */ split(&buf, ':'); @@ -441,7 +646,7 @@ server_event_cb(picoev_loop *loop, int fd, int revents, void *cb_arg) if (ids[i] > 0) writeall(events[ids[i]].fd, msg); } - return; + return 0; } else if (strcmp(cmd, "KICK") == 0) { /* :<nick_which_is_kicking>!~user@host KICK <channel> <nick_which_has_been_kicked> :<kick_msg> */ int *ids; @@ -453,20 +658,20 @@ server_event_cb(picoev_loop *loop, int fd, int revents, void *cb_arg) /* delete whole network if it is the linker */ if (strcmp(user, lnick) == 0) { - net_del(loop, networks[netid].name); - return; + net_del(networks[netid].name); + return 0; } /* delete the user if the message from the same network */ - if ((ids = clone_get_user_ids(user, netid)) == NULL) { + if ((ids = user_event_ids(user, netid)) == NULL) { nick_add_symb(user, netid); - user_del(loop, user, msg); - return; + user_del(user, msg); + return 0; } /* close the kicked fd */ writeall(fd, msg); - event_del(loop, id); + event_del(id); /* * send notice in the channel through linker @@ -485,32 +690,32 @@ server_event_cb(picoev_loop *loop, int fd, int revents, void *cb_arg) "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; + return 0; } printbuffer: - printf("%d: %s\n", fd, backup); + return 0; } void -net_add(picoev_loop *loop, char *name, char *symb, char *host, char *port, char *chan) +net_add(char *name, char *symb, char *host, char *port, char *chan) { - int i, fd; struct network *n; + int i, fd; /* if name, symbol or configuration already exists */ for (i = 0; i < netlen; i++) { if (strcmp(networks[i].name, name) == 0) { - printf("error: network name '%s' already exists\n", name); + warnx("network name '%s' already exists", name); return; } if (strcmp(networks[i].symb, symb) == 0) { - printf("error: network symbol '%s' already exists\n", symb); + warnx("network symbol '%s' already exists", symb); return; } if ((strcmp(networks[i].host, host) == 0) && (strcmp(networks[i].port, port) == 0) && (strcmp(networks[i].chan, chan) == 0)) { - printf("error: network configuration already exists\n"); + warnx("network configuration already exists"); return; } } @@ -518,11 +723,11 @@ net_add(picoev_loop *loop, char *name, char *symb, char *host, char *port, char /* resize if full */ if (netlen == netcap) { Htiter it = {0}; - networks = erecalloc(networks, (size_t)netcap, NET_ADDEND, - sizeof(struct network)); + networks = realloc0(networks, sizeof(struct network) * (size_t)netcap, + sizeof(struct network) * (size_t)(netcap + NET_ADDEND)); while (htiterate(users, &it)) - it.node->val = erecalloc(it.node->val, (size_t)netcap, - NET_ADDEND, sizeof(int)); + it.node->val = realloc0(it.node->val, sizeof(int) * (size_t)netcap, + sizeof(int) * (size_t)(netcap + NET_ADDEND)); netcap += NET_ADDEND; } @@ -531,19 +736,20 @@ net_add(picoev_loop *loop, char *name, char *symb, char *host, char *port, char return; /* add a network */ n = &networks[netlen]; - n->id = event_add(loop, fd, netlen, NULL); - n->join = 0; + 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; netlen++; + printf("%d: network '%s' added\n", fd, name); } void -net_del(picoev_loop *loop, char *name) +net_del(char *name) { int i, netid, *ids; @@ -559,7 +765,7 @@ net_del(picoev_loop *loop, char *name) } } if (netid == -1) { - printf("error: network '%s' doesn't exist\n", name); + warnx("network '%s' doesn't exist", name); return; } @@ -571,14 +777,14 @@ net_del(picoev_loop *loop, char *name) ids = (int *)it.node->val; /* delete all the users of deleting network */ if (ids[netid] == -1) { - user_del(loop, it.node->key, msg); + user_del(it.node->key, msg); /* this node is deleted */ it = lastit; /* delete the clones */ } else { if (ids[netid] > 0) { writeall(events[ids[netid]].fd, msg); - event_del(loop, ids[netid]); + event_del(ids[netid]); } /* swap last with current one */ ids[netid] = ids[netlen-1]; @@ -587,13 +793,13 @@ net_del(picoev_loop *loop, char *name) } /* set last netid of events to current netid. */ - for (i = 0; i < evlen; i++) { + for (i = 0; i < evslen; i++) { if (events[i].netid == netlen-1) events[i].netid = netid; } writeall(events[networks[netid].id].fd, msg); - event_del(loop, networks[netid].id); + event_del(networks[netid].id); net_del_raw(netid); printf("%d: network '%s' deleted\n", events[networks[netid].id].fd, name); /* swap the network with the last */ @@ -613,167 +819,162 @@ net_del_raw(int netid) } void -net_update(picoev_loop *loop, int netid) +user_add(char *unick, int netid, int clone) { - Htiter it = {0}; - while (htiterate(users, &it)) - ((int *)it.node->val)[netid] = clone_add(loop, it.node->key, - netid); -} - -void -user_add(picoev_loop *loop, char *unick, int netid) -{ - int i, *ids; - size_t len; + size_t len; char *nick; + int *ids, i; len = strlen(unick) + strlen(networks[netid].symb) + 2 + 1; /* too long nick */ - if (len - 1 > NICK_LEN) { - printf("error: user nick '%s' is too big\n", unick); - return; - } + if (len - 1 > NICK_LEN) + warnx("nick '%s' is too big", unick); /* resize hash table if storage is low */ if ((users->cap - users->len) < USER_ADDEND) htresize(users, users->cap + USER_ADDEND); - printf("useradd: %s\n", unick); - /* allocate a new user */ nick = ecalloc(len, sizeof(char)); ids = ecalloc((size_t)netcap, sizeof(int)); sprintf(nick, "%s[%s]", unick, networks[netid].symb); + ids[netid] = -1; - /* clone the user on all other network */ - for (i = 0; i < netlen; i++) { - if (networks[i].join == 0) - continue; - if (i == netid) { - ids[i] = -1; - } else { - /* ids[i] = clone_add(loop, nick, i); */ + printf("useradd: %s\n", nick); + + if (clone) { + /* clone the user on all other network */ + for (i = 0; i < netlen; i++) { + if (networks[i].ready == FALSE) + continue; + if (i != netid) { + ids[i] = clone_add(nick, i); + printf("%d: %s\n", events[ids[i]].fd, nick); + } } } + /* insert it to the users hash table */ - if (htinsert(users, nick, ids) == -1) { + if (htinsert(users, nick, ids) == -1) /* this shouldn't happen as it was already checked */ - printf("error: user '%s' already exists\n", nick); - clean_exit(loop, 1); - } + errx(1, "user '%s' already exists", nick); } void -user_del(picoev_loop *loop, char *nick, char *msg) +user_del(char *nick, char *msg) { - int i; - int *ids = user_get_ids(nick); + int i, *ids; + if ((ids = htsearch(users, nick)) == NULL) + errx(1, "user '%s' 1 doesn't exists", nick); for (i = 0; i < netlen; i++) { - if (ids[i] > 0) { + if (ids[i] <= 0) + continue; + if (events[ids[i]].ready == TRUE) writeall(events[ids[i]].fd, msg); - event_del(loop, ids[i]); - } + event_del(ids[i]); } htremove(users, nick); } int * -user_get_ids(char *user) -{ - int *ids; - if ((ids = htsearch(users, user)) == NULL) { - printf("error: cannot find user '%s'\n", user); - exit(1); - } - return ids; -} - -int * -clone_get_user_ids(char *nick, int netid) +user_event_ids(char *nick, int netid) { - unsigned int suffix; + unsigned int s; int *ids = NULL; /* count suffix */ - for (suffix = 0; nick[strlen(nick)-suffix-1] == '_'; suffix++); + for (s = 0; nick[strlen(nick)-s-1] == '_'; s++); /* remove suffix */ - if (suffix > 0) - nick[strlen(nick)-suffix] = '\0'; + 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)suffix)) + if ((ids != NULL) && (events[ids[netid]].suffix != (int)s)) ids = NULL; /* add suffix back */ - if (suffix > 0) + if (s > 0) nick[strlen(nick)] = '_'; return ids; } int -clone_add(picoev_loop *loop, char *nick, int netid) +clone_add(char *nick, int netid) { - int fd; - struct network *n = &networks[netid]; + struct network *n; + int fd; - if ((fd = dial(n->host, n->port)) == -1) + n = &networks[netid]; + if ((fd = dial(n->host, n->port)) == -1) { + warn("enable to connect to %s\n", n->host); return -1; - return event_add(loop, fd, netid, nick); + } + return event_add(fd, netid, nick); } int -event_add(picoev_loop *loop, int fd, int netid, char *user) +event_add(int fd, int netid, char *user) { - int i = evlen; + int i = evslen; - if (evlen == evcap) { - events = erecalloc(events, (size_t)evcap, EVENT_ADDEND, - sizeof(struct event)); - evcap += EVENT_ADDEND; + if (evslen == evscap) { + events = realloc0(events, sizeof(struct event) * (size_t)evscap, + sizeof(struct event) * (size_t)(evscap + EVENT_ADDEND)); + evscap += EVENT_ADDEND; } - events[i].fd = fd; - events[i].netid = netid; - events[i].user = user; + events[i].fd = fd; + events[i].netid = netid; + events[i].user = user; events[i].suffix = 0; - picoev_add(loop, fd, PICOEV_WRITE, 0, server_event_cb, &events[i]); + events[i].time = 0; + events[i].ready = FALSE; + + fd_register(fd, EV_WRITE); - return evlen++; + 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++; } void -event_del(picoev_loop *loop, int id) +event_del(int id) { int *ids; - int l = evlen - 1; /* last id */ + int l = evslen - 1; /* last id */ /* swap id */ - if (events[l].user == NULL) { /* if linker */ + if (events[l].user == NULL) { /* if linker */ networks[events[l].netid].id = id; - } else { /* if user */ - ids = user_get_ids(events[l].user); + } 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; } /* disable id */ - if (events[id].user == NULL) { /* if linker */ + if (events[id].user == NULL) { /* if linker */ networks[events[id].netid].id = -2; - } else { /* if user */ - ids = user_get_ids(events[id].user); + } 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; } - picoev_del(loop, events[id].fd); close(events[id].fd); events[id] = events[l]; - events[id] = events[l]; - evlen--; + evslen--; + fdtoid[events[id].fd] = id; } void @@ -805,7 +1006,7 @@ privmsg_update(char *dst, char *src, int netid) } /* check if the word is nick */ - if (clone_get_user_ids(src, netid) != NULL) + if (user_event_ids(src, netid) != NULL) *strrchr(src, '[') = '\0'; strcat(dst, src); @@ -816,37 +1017,6 @@ privmsg_update(char *dst, char *src, int netid) } void -clean_exit(picoev_loop *loop, int status) -{ - int i; - snprintf(msg, sizeof(msg), "QUIT :linker shutting down\r\n"); - for (i = 0; i < evlen; i++) { - writeall(events[i].fd, msg); - event_del(loop, i); - } - picoev_destroy_loop(loop); - picoev_deinit(); - - /* delete all the users */ - htdestroy(users); - - /* delete all the networks */ - for (i = 0; i < netlen; i++) - net_del_raw(i); - - free(networks); - free(events); - - if (status == 0) { - printf("exit successfully\n"); - exit(0); - } else { - printf("aborted\n"); - exit(1); - } -} - -void print_table(void) { int i, *ids, diff, tabs; diff --git a/picoev b/picoev @@ -1 +0,0 @@ -Subproject commit ff85d9ef578842a40f7c91d2544b7932cec74b9d diff --git a/util.c b/util.c @@ -3,20 +3,22 @@ * See COPYING file for more information. */ -#include <ctype.h> +#include <sys/socket.h> +#include <sys/stat.h> + #include <errno.h> #include <fcntl.h> -#include <stdarg.h> +#include <netdb.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <netdb.h> -#include <poll.h> #include <unistd.h> +#ifdef __gnu_linux__ +#include <bsd/err.h> +#else +#include <err.h> +#endif /* * ecalloc - calloc with error handling @@ -25,29 +27,24 @@ void * ecalloc(size_t nmemb, size_t size) { void *p; - if ((p = calloc(nmemb, size)) == NULL) { - printf("error: calloc: %s\n", strerror(errno)); - exit(1); - } + if ((p = calloc(nmemb, size)) == NULL) + err(1, "calloc"); return p; } /* - * erecalloc -- allocate more memory, zero new memory, handle error + * realloc0 -- allocate more memory and zero new memory * ptr - pointer of the old memory - * omemb - no. of member of old pointer - * nmemb - no. of member to extend - * size - size of one member + * osize - size of old memory + * nsize - size to new memory */ void * -erecalloc(void *ptr, size_t omemb, size_t nmemb, size_t size) +realloc0(void *ptr, size_t osize, size_t nsize) { void *p; - if ((p = realloc(ptr, (omemb + nmemb) * size)) == NULL) { - printf("error: realloc: %s\n", strerror(errno)); - exit(1); - } - /* memset(p + omemb * size, 0, nmemb * size); */ + if ((p = realloc(ptr, nsize)) == NULL) + err(1, "realloc"); + memset(((char *)p + osize), 0, nsize - osize); return p; } @@ -77,19 +74,15 @@ fifo_open(char *path) /* make fifo if it doesn't exists */ if (lstat(path, &st) != -1) { - if (!(st.st_mode & S_IFIFO)) { - printf("error: '%s' is not a fifo file\n", path); - return -1; - } - } else if (mkfifo(path, S_IRWXU) != 0) { - printf("error: failed to create fifo file '%s'\n", path); - return -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); } - /* open fifo */ fd = open(path, O_RDONLY | O_NONBLOCK, 0); if (fd == -1) - printf("error: cannot open() '%s'\n", path); + err(1, "open: %s", path); return fd; } @@ -101,10 +94,8 @@ writeall(int fd, char *buf) left = (ssize_t)strlen(buf); sent = 0; while (sent < left) { - if ((n = write(fd, buf+sent, (size_t)left)) == -1) { - printf("error: write failed: %s\n", strerror(errno)); - return -1; - } + if ((n = write(fd, buf+sent, (size_t)left)) == -1) + err(1, "%d: write", fd); sent += n; left -= n; } @@ -112,27 +103,6 @@ writeall(int fd, char *buf) } int -connect_wait(int s) -{ - struct pollfd pfd[1]; - int error = 0; - socklen_t len = sizeof(error); - - pfd[0].fd = s; - pfd[0].events = POLLOUT; - - if (poll(pfd, 1, -1) == -1) - return -1; - if (getsockopt(s, SOL_SOCKET, SO_ERROR, &error, &len) == -1) - return -1; - if (error != 0) { - errno = error; - return -1; - } - return 0; -} - -int dial(char *host, char *port) { static struct addrinfo hints, *res = NULL, *res0; @@ -143,26 +113,27 @@ dial(char *host, char *port) hints.ai_socktype = SOCK_STREAM; if ((r = getaddrinfo(host, port, &hints, &res0)) != 0) { - printf("error: getaddrinfo: %s\n", gai_strerror(r)); + warnx("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) + if ((fd = socket(res->ai_family, res->ai_socktype, + res->ai_protocol)) == -1) continue; if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) { - printf("error: fcntl: %s\n", strerror(errno)); + warn("fnctl"); continue; } if ((connect(fd, res->ai_addr, res->ai_addrlen) == -1) - && (errno == EINPROGRESS)) + && (errno == EINPROGRESS)) break; - printf("error: connect: %s\n", strerror(errno)); + warn("connect"); close(fd); fd = -1; } if (fd == -1) - printf("error: cannot connect to host '%s'\n", host); + warnx("cannot connect to %s", host); freeaddrinfo(res0); return fd; @@ -171,15 +142,34 @@ dial(char *host, char *port) ssize_t readline(int fd, char *buffer, size_t size) { - ssize_t n, i = 0; + static int left_fd; + static char left_buf[1024]; + + size_t i = 0; char c; + ssize_t l; + + if (fd == left_fd && strlen(left_buf) > 0) { + strlcpy(buffer, left_buf, size); + *left_buf = '\0'; + i = strlen(buffer); + } do { - if ((n = read(fd, &c, sizeof(char))) != sizeof(char)) - return n; + if ((l = read(fd, &c, sizeof(char))) != sizeof(char)) { + if (l == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) { + buffer[i] = '\0'; + left_fd = fd; + strlcpy(left_buf, buffer, size); + *buffer = '\0'; + return 1; + } + return l; + } buffer[i++] = c; - } while ((i < (ssize_t)size) && (c != '\n') && (c != '\0')); + } while ((i < size) && (c != '\r') && (c != '\n')); buffer[i-1] = '\0'; - return i; + return (ssize_t)i; + }