ticl

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

commit 66e66acea622b6183cdf8767c4d268bfe100bce0
parent 8df402e53d02ca2c0b976715ce57c28750dd99d9
Author: libredev <libredev@ircforever.org>
Date:   Sat, 19 Nov 2022 23:30:10 +0530

fix duplicate clone and replace select() with poll()

Diffstat:
MMakefile | 2+-
MREADME | 52++++++++++++++++++++--------------------------------
Mmain.c | 550++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mutil.c | 79++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
4 files changed, 364 insertions(+), 319 deletions(-)

diff --git a/Makefile b/Makefile @@ -4,7 +4,7 @@ PREFIX = /usr/local CC = cc -CFLAGS = -g -std=c89 -Wall -Wextra -pedantic -Wconversion\ +CFLAGS = -g -std=c89 -Wall -Wextra -pedantic -Wfatal-errors -Wconversion\ -Wstrict-prototypes -Wold-style-definition\ -D_POSIX_C_SOURCE=200809L #CFLAGS += -fsanitize=address -fno-omit-frame-pointer diff --git a/README b/README @@ -70,6 +70,10 @@ To start the program with 'in' as the fifo file: $ ./ticl ./in +Or, to start the program with the log printing to a file: + + $ ./ticl ./in > ticl.log + To link channel #test20 from libera and channel #test21 from ircnow: $ echo 'netadd libera L irc.libera.chat 6667 #test20' > ./in @@ -79,6 +83,17 @@ To unlink channel ircnow: $ echo 'netdel ircnow' > ./in +To list all the sockets (connections): + + $ echo 'print' > ./in + + # -1 indicate original user + # -2 indicate kicked clone + +To list all the users: + + $ echo 'users' > ./in + To shutdown the program: $ echo exit > ./in @@ -163,36 +178,9 @@ This work is dedicated to the public domain. See COPYING file for more information. -FAQ ---- - -Why not GPL? ------------- - -I was a big fan of GPL but slowly I realized that people should choose free -software on their own intention and not by force. They should understand that -free culture is the way to the future. But I don't hate GPL and I think it is -a great license for new technologies and innovations that can be exploited by -corporate. - - -Why not MIT/BSD? ----------------- - -I hate attribution licenses like MIT, BSD or any permissive license because -I think the attribution requirement is complete nonsense. - - -Why shouldn't you call it open source? --------------------------------------- - -I hate the term 'open source' because it has nothing to do with free software -and its philosophy. It is a misleading term used by corporate that refer to -open collaboration. - -But that's not why you shouldn't call it open source, but because the -Open Source Initiative (OSI) doesn't consider public domain software to be -open source. For more information, visit https://opensource.org/node/878. +Note +---- -It is free software because the FSF considers the public domain software to be -free software. +It is a free software and please do not 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/main.c b/main.c @@ -8,25 +8,27 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <sys/select.h> -#include <sys/stat.h> +#include <poll.h> #include <unistd.h> #include "htable.h" #include "util.c" #define BUFFER_LEN 1024 -#define MAX_FD 1024 - #define MAX_NICK_LEN 16 + +#define FD_ADDEND 100 +#define POLLFD_ADDEND 100 #define NET_ADDEND 10 -#define USER_ADDEND 5 +#define USER_ADDEND 100 -#define FD_TYPE_LINKER 1 -#define FD_TYPE_CLONE 2 +#define FIFO_FD 1 +#define LINKER_FD 2 +#define CLONE_FD 3 typedef struct { int fd; /* fd of linker */ + int join; /* is joined */ char *name; /* name */ char *symb; /* symbol */ char *host; /* hostname */ @@ -34,34 +36,41 @@ typedef struct { char *chan; /* channel */ } Network; -static int isrunning = 1; +/* user defined data for each fd */ +struct fdData { + int netid; /* net index */ + int type; /* fd type */ + int suffix; /* fd suffix count */ +}; -static fd_set fdset; /* fd_set (select) */ -static int nfds; /* number of fds */ +static struct pollfd *pfdset; /* pollfd set */ +static struct fdData *fddata; /* fd user data set */ +static int pfdlen; /* pollfd length */ +static int pfdcap; /* pollfd capacity */ +static int *fdtoid; /* fd to pfdset index */ +static int fdscap; /* fds capacity */ -static int fdnet [MAX_FD]; /* fd -> network index */ -static int fdtype [MAX_FD]; /* fd -> fd type */ -static int fdsuf [MAX_FD]; /* fd -> nick suffix */ +static Network *networks; /* linked list of networks */ +static int netlen; /* current network length */ +static int netcap; /* total memory allocated */ +static Htable *users; /* users-clones hash table */ -static Network *networks; /* linked list of networks */ -static int netlen; /* current network length */ -static int netcap; /* total memory allocated */ -static Htable *users; +char msg[BUFFER_LEN]; +static int isrunning = 1; /* functions prototype */ -void handle_fifo_input(int); +void handle_fifo_input(int, char *); void handle_server_output(int); void net_add(char *, char *, char *, char *, char *); void net_del(char *); void net_del_raw(int); -void net_resize(void); -void net_update(void); +void net_update(int); void user_add(char *, int); void user_del(char *, char *); int *user_fds(char *, int); int clone_add(int, char *); -int fd_add(char *, char *); -void fd_del(int, char *); +void fd_add(int, int, int, int); +void fd_del(int); void nick_add_symb(char *, int); void privmsg_update(char *, char *, int); void terminate(int); @@ -73,9 +82,7 @@ void print_border(void); int main(int argc, char *argv[]) { - int fifo_fd, r, i; - fd_set rdset; - struct stat fifo_st; + int i, n; /* set stdout to unbufferd */ setvbuf(stdout, NULL, _IONBF, 0); @@ -86,10 +93,14 @@ main(int argc, char *argv[]) return 1; } - /* init networks and users */ + /* init global variables */ + pfdcap = POLLFD_ADDEND; + pfdset = ecalloc((size_t)pfdcap, sizeof(struct pollfd)); + fddata = ecalloc((size_t)pfdcap, sizeof(struct fdData)); + fdscap = FD_ADDEND; + fdtoid = ecalloc((size_t)fdscap, sizeof(int)); netcap = NET_ADDEND; networks = ecalloc((size_t)netcap, sizeof(Network)); - /* * hash table of users * key -> <nickname> + '[' + <network_symbol> + ']' @@ -98,53 +109,28 @@ main(int argc, char *argv[]) */ users = htcreate((KeyLenFn *)strlen, (KeyCmpFn *)strcmp, free, free, USER_ADDEND); - /* init fdnet array to -1 */ - for (i = 0; i < MAX_FD; i++) - fdnet[i] = -1; - - /* crate or/and open fifo */ - if (stat(argv[1], &fifo_st) == 0) { - if (!(fifo_st.st_mode & S_IFIFO)) { - printf("error: '%s' is not a fifo file\n", argv[1]); - return 1; - } - } else if (mkfifo(argv[1], S_IRWXU) != 0) { - printf("error: failed to create fifo file '%s'\n", argv[1]); - return 1; - } - fifo_fd = open(argv[1], O_RDWR); - if (fifo_fd < 0) { - printf("error: cannot open() '%s'\n", argv[1]); - return 1; - } - - /* initialize fdset */ - FD_ZERO(&fdset); - FD_SET(fifo_fd, &fdset); - nfds = fifo_fd + 1; + /* add fifo_fd */ + fd_add(fifo_open(argv[1]), 0, FIFO_FD, 0); /* select loop */ while (isrunning) { - rdset = fdset; - r = select(nfds, &rdset, 0, 0, NULL); - if (r < 0) { - if (errno == EINTR) - continue; - printf("error: select: %s\n", strerror(errno)); - terminate(1); - } else if (r == 0) { - printf("error: select timeout\n"); + if ((n = poll(pfdset, (nfds_t)pfdlen, -1)) == -1) { + printf("error: poll: %s\n", strerror(errno)); terminate(1); } - - for (i = 0; i < nfds; i++) { - if (!FD_ISSET(i, &rdset)) + for (i = 0; i < pfdlen; i++) { + if (!(pfdset[i].revents & (POLLIN|POLLHUP))) continue; - if (i == fifo_fd) - handle_fifo_input(i); + printf("poll: %d\n", pfdset[i].fd); + if (fddata[i].type == FIFO_FD) + handle_fifo_input(i, argv[1]); else handle_server_output(i); - break; /* one fd at a time because of FD_CLR */ + /* + * handle one ready fd at one time because if we + * close upcoming ready fd, it cause infinite loop. + */ + break; } } terminate(0); @@ -152,15 +138,22 @@ main(int argc, char *argv[]) } void -handle_fifo_input(int fd) +handle_fifo_input(int id, char *path) { char buffer[BUFFER_LEN]; char *buf; char *cmd; - - if (readline(fd, buffer, sizeof(buffer)) < 1) { - printf("error: %d: failed to read from fifo: %s\n", fd, strerror(errno)); - terminate(1); + ssize_t n; + + /* if failed to read data */ + if ((n = readline(pfdset[id].fd, buffer, sizeof(buffer))) < 1) { + if (n == 0) { /* restart again */ + fd_del(pfdset[id].fd); + fd_add(fifo_open(path), 0, FIFO_FD, 0); + } else if ((errno != EAGAIN) && (errno != EINTR)) { + printf("error: read: %s\n", strerror(errno)); + } + return; } /* printf("fifo: %s\n", buffer); */ @@ -196,38 +189,46 @@ handle_fifo_input(int fd) } void -handle_server_output(int fd) +handle_server_output(int id) { - char backup [BUFFER_LEN]; - char buffer [BUFFER_LEN]; + char buffer [BUFFER_LEN]; + char backup [BUFFER_LEN]; char linker_nick [MAX_NICK_LEN]; char *buf; char *cmd; char *nick; - int i; + int i, netid; + ssize_t n; + + /* if failed to read data */ + if ((n = readline(pfdset[id].fd, buffer, sizeof(buffer))) < 1) { + if (n == 0) { + printf("error: remote host closed connection: %s\n", strerror(errno)); + /* fd_del(pfdset[id].fd); */ + } else if ((errno != EAGAIN) && (errno != EINTR)) { + printf("error: read: %s\n", strerror(errno)); + } + return; + } - if (readline(fd, buffer, sizeof(buffer)) < 1) { - printf("error: %d: remote host closed connection: %s\n", fd, strerror(errno)); - terminate(1); + /* remove CRLFs */ + for (i = 0; i < (int)strlen(buffer); i++) { + if (buffer[i] == '\r' || buffer[i] == '\n') { + buffer[i] = '\0'; + break; + } } /* clone the buffer */ strcpy(backup, buffer); buf = buffer; + netid = fddata[id].netid; /* set linker nick */ strcpy(linker_nick, "linker"); - for (i = 0; i < fdsuf[fd]; i++) + for (i = 0; i < fddata[id].suffix; i++) strcat(linker_nick, "_"); - /* remove CRLFs */ - for (i = 0; i < (int)strlen(buf); i++) { - if (buf[i] == '\r' || buf[i] == '\n') { - buf[i] = '\0'; - break; - } - } - /* first column */ cmd = split(&buf, ' '); if (strcmp(cmd, "NOTICE") == 0) { @@ -235,7 +236,8 @@ handle_server_output(int fd) } else if (strcmp(cmd, "ERROR") == 0) { goto printbuffer; } else if (strcmp(cmd, "PING") == 0) { - fdprintf(fd, "PONG %s\r\n", buf); + snprintf(msg, sizeof(msg), "PONG %s\r\n", buf); + writeall(pfdset[id].fd, msg); return; } /* strip nick from first column */ @@ -271,38 +273,41 @@ handle_server_output(int fd) } else if (strcmp(cmd, "433") == 0) { /* Nickname already in use */ split(&buf, ' '); nick = split(&buf, ' '); - strcat(nick, "_"); - fdsuf[fd]++; - if (strlen(nick) > MAX_NICK_LEN) { - /* remove suffix from the nick */ - nick[strlen(nick) - (size_t)fdsuf[fd]] = '\0'; + if (strlen(nick)+1 > MAX_NICK_LEN) { printf("error: cannot append suffix, nick '%s' is too big\n", nick); - if (strcmp(nick, "linker") == 0) { - net_del(networks[fdnet[fd]].name); + if (strcmp(nick, linker_nick) == 0) { + net_del(networks[netid].name); } else { - user_del(nick, "nick is too big"); + snprintf(msg, sizeof(msg), "QUIT :nick is too big\r\n"); + user_del(nick, msg); } } else { - fdprintf(fd, "NICK %s\r\n", nick); + strcat(nick, "_"); + fddata[id].suffix++; + snprintf(msg, sizeof(msg), "NICK %s\r\n", nick); + writeall(pfdset[id].fd, msg); } return; } else if (strcmp(cmd, "001") == 0) { - fdprintf(fd, "JOIN %s\r\n", networks[fdnet[fd]].chan); + snprintf(msg, sizeof(msg), "JOIN %s\r\n", networks[netid].chan); + writeall(pfdset[id].fd, msg); return; } else if (strcmp(cmd, "PRIVMSG") == 0) { - char msg[BUFFER_LEN] = ""; + char privmsg[BUFFER_LEN] = ""; int *fds; - if (fdtype[fd] == FD_TYPE_LINKER) { - nick_add_symb(nick, fd); + if (fddata[id].type == LINKER_FD) { + nick_add_symb(nick, netid); if ((fds = htsearch(users, nick)) == NULL) return; split(&buf, ':'); /* set buf to msg */ - privmsg_update(msg, buf, fd); + privmsg_update(privmsg, buf, netid); for (i = 0; i < netlen; i++) { - if (fds[i] > 0) - fdprintf(fds[i], "PRIVMSG %s :%s\r\n", networks[i].chan, msg); + if (fds[i] > 0) { + snprintf(msg, sizeof(msg), "PRIVMSG %s :%s\r\n", networks[i].chan, privmsg); + writeall(fds[i], msg); + } } } else { char *netsymb; @@ -312,7 +317,7 @@ handle_server_output(int fd) if ((user[0] == '#') || (user[0] == '&')) return; - nick_add_symb(nick, fd); + nick_add_symb(nick, netid); if ((fds = htsearch(users, nick)) == NULL) return; @@ -328,13 +333,14 @@ handle_server_output(int fd) } split(&buf, ':'); /* set buf to msg */ - privmsg_update(msg, buf, fd); - fdprintf(fds[i], "PRIVMSG %s :%s\r\n", user, msg); + privmsg_update(privmsg, buf, netid); + snprintf(msg, sizeof(msg), "PRIVMSG %s :%s\r\n", user, privmsg); + writeall(fds[i], msg); } return; } /* these messages are handled by linker */ - if (fdtype[fd] == FD_TYPE_CLONE) { + if (fddata[id].type == CLONE_FD) { if ((strcmp(cmd, "353") == 0) || (strcmp(cmd, "JOIN") == 0) || (strcmp(cmd, "QUIT") == 0) @@ -345,7 +351,8 @@ handle_server_output(int fd) } else if (strcmp(cmd, "353") == 0) { char *nick; split(&buf, ':'); - net_update(); + networks[netid].join = 1; + net_update(netid); while (*(nick = split(&buf, ' ')) != '\0') { if (*nick == '@' || *nick == '&' @@ -355,37 +362,39 @@ handle_server_output(int fd) || *nick == '\\') nick++; if (strcmp(nick, linker_nick) != 0) - user_add(nick, fd); + user_add(nick, netid); } return; } else if (strcmp(cmd, "JOIN") == 0) { if ((strcmp(nick, linker_nick) != 0) - && (user_fds(nick, fd) == NULL)) /* if not clone */ - user_add(nick, fd); + && (user_fds(nick, netid) == NULL)) /* if not clone */ + user_add(nick, netid); return; } else if ((strcmp(cmd, "QUIT") == 0) || (strcmp(cmd, "PART") == 0)) { - nick_add_symb(nick, fd); - user_del(nick, buf); + nick_add_symb(nick, netid); + snprintf(msg, sizeof(msg), "QUIT :%s\r\n", buf); + user_del(nick, msg); return; } else if (strcmp(cmd, "NICK") == 0) { int *fds, i; char *newnick; - nick_add_symb(nick, fd); + nick_add_symb(nick, netid); if ((fds = htsearch(users, 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[fdnet[fd]].symb) + 2 + 1, sizeof(char)); - sprintf(newnick, "%s[%s]", buf, networks[fdnet[fd]].symb); + newnick = ecalloc(strlen(buf) + strlen(networks[netid].symb) + 2 + 1, sizeof(char)); + sprintf(newnick, "%s[%s]", buf, networks[netid].symb); htsetkey(users, nick, newnick); + snprintf(msg, sizeof(msg), "NICK %s\r\n", newnick); for (i = 0; i < netlen; i++) { if (fds[i] > 0) - fdprintf(fds[i], "NICK %s\r\n", newnick); + writeall(fds[i], msg); } return; } else if (strcmp(cmd, "KICK") == 0) { @@ -393,45 +402,49 @@ handle_server_output(int fd) int *fds; char *chan = split(&buf, ' '); char *user = split(&buf, ' '); - char quit_msg[BUFFER_LEN]; /* set the quit msg */ - snprintf(quit_msg, BUFFER_LEN, "kicked by %s", nick); + snprintf(msg, sizeof(msg), "QUIT : kicked by %s\r\n", nick); + /* delete whole network if it is the linker */ if (strcmp(user, linker_nick) == 0) { - net_del(networks[fdnet[fd]].name); + net_del(networks[netid].name); return; } /* delete the user if the message from the same network */ - if ((fds = user_fds(user, fd)) == NULL) { - nick_add_symb(user, fd); - user_del(user, quit_msg); + if ((fds = user_fds(user, netid)) == NULL) { + nick_add_symb(user, netid); + user_del(user, msg); return; } - /* remove netsymb and suffix */ - *strrchr(user, '[') = '\0'; - - /* get the original user fd index */ + /* close the kicked fd */ + writeall(fds[netid], msg); + fd_del(fds[netid]); + fds[netid] = -2; + + /* + * send notice in the channel through linker + */ + /* get the original user netid */ for (i = 0; i < netlen; i++) { if (fds[i] == -1) break; } - /* set buf to msg */ - split(&buf, ':'); - /* send notice in the channel through linker */ - fdprintf(networks[i].fd, "PRIVMSG %s :%s was kicked out by %s from network %s %s [%s]\r\n", - networks[i].chan, user, nick, networks[fdnet[fd]].name, chan, buf); - - /* close the kicked fd */ - fd_del(fds[fdnet[fd]], quit_msg); - fds[fdnet[fd]] = -2; + 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(networks[i].fd, msg); return; } printbuffer: - printf("%d: %s\n", fd, backup); + printf("%d: %s\n", pfdset[id].fd, backup); } void @@ -440,7 +453,7 @@ net_add(char *name, char *symb, char *host, char *port, char *chan) int i, fd; Network *n; - /* if name or symbol or configuration already exists */ + /* 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); @@ -451,29 +464,35 @@ net_add(char *name, char *symb, char *host, char *port, char *chan) return; } if ((strcmp(networks[i].host, host) == 0) - && (strcmp(networks[i].port, port) == 0) - && (strcmp(networks[i].chan, chan) == 0)) { + && (strcmp(networks[i].port, port) == 0) + && (strcmp(networks[i].chan, chan) == 0)) { printf("error: network configuration already exists\n"); return; } } - /* resize if no space */ - if (netlen == netcap) - net_resize(); + /* resize if full */ + if (netlen == netcap) { + Htiter it = {0}; + networks = erecalloc(networks, (size_t)netcap, NET_ADDEND, sizeof(Network)); + while (htiterate(users, &it)) + it.node->val = erecalloc(it.node->val, (size_t)netcap, NET_ADDEND, sizeof(int)); + netcap += NET_ADDEND; + } /* connect */ - if ((fd = fd_add(host, port)) == -1) + if ((fd = dial(host, port)) == -1) return; + fd_add(fd, netlen, LINKER_FD, 0); /* send NICK and USER commands */ - fdprintf(fd, "NICK linker\r\n"); - fdprintf(fd, "USER linker 0 * :linker\r\n"); + snprintf(msg, sizeof(msg), "NICK linker\r\n"); + writeall(fd, msg); + snprintf(msg, sizeof(msg), "USER linker 0 * :linker\r\n"); + writeall(fd, msg); /* add a network */ - fdnet[fd] = netlen; - fdtype[fd] = FD_TYPE_LINKER; - n = &networks[netlen]; n->fd = fd; + n->join = 0; n->name = strdup(name); n->symb = strdup(symb); n->host = strdup(host); @@ -486,64 +505,66 @@ net_add(char *name, char *symb, char *host, char *port, char *chan) void net_del(char *name) { - int i, index, *fds; - char quit_msg [BUFFER_LEN]; + int i, netid, *fds; Htiter it = {0}; /* current iterator */ Htiter lastit = {0}; /* last iterator */ - /* check if the name exists */ - index = -1; + /* get netid */ + netid = -1; for (i = 0; i < netlen; i++) { if (strcmp(name, networks[i].name) == 0) { - index = i; + netid = i; break; } } - if (index == -1) { - printf("error: network name '%s' doesn't exist\n", name); + if (netid == -1) { + printf("error: network '%s' doesn't exist\n", name); return; } /* set the quit msg */ - snprintf(quit_msg, BUFFER_LEN, "unlinking network %s", name); - - /* set all fds with last network index to the current index. */ - for (i = 0; i < nfds; i++) { - if (fdnet[i] == netlen-1) - fdnet[i] = index; - } + snprintf(msg, sizeof(msg), "QUIT :unlinking network %s\r\n", name); + /* reconstruct the user-clones table */ while (htiterate(users, &it)) { fds = (int *)it.node->val; - /* delete the user */ - if (fds[index] == -1) { - user_del(it.node->key, quit_msg); - /* this node is deleted and has no next */ + /* delete all the users of deleting network */ + if (fds[netid] == -1) { + user_del(it.node->key, msg); + /* this node is deleted */ it = lastit; - /* delete the clone */ + /* delete the clones */ } else { - if (fds[index] > 0) - fd_del(fds[index], quit_msg); - /* swap last index with the index */ - fds[index] = fds[netlen-1]; - fds[netlen-1] = 0; + if (fds[netid] > 0) { + writeall(fds[netid], msg); + fd_del(fds[netid]); + } + /* swap last with current one */ + fds[netid] = fds[netlen-1]; } lastit = it; } - fd_del(networks[index].fd, quit_msg); - net_del_raw(index); - printf("%d: network '%s' deleted\n", networks[index].fd, name); + /* set pollfds netid with last netid to current netid. */ + for (i = 0; i < pfdlen; i++) { + if (fddata[i].netid == netlen-1) + fddata[i].netid = netid; + } + + writeall(networks[netid].fd, msg); + fd_del(networks[netid].fd); + net_del_raw(netid); + printf("%d: network '%s' deleted\n", networks[netid].fd, name); /* swap the network with the last */ - networks[index] = networks[netlen-1]; + networks[netid] = networks[netlen-1]; netlen--; } void -net_del_raw(int index) +net_del_raw(int netid) { - Network *n = &networks[index]; + Network *n = &networks[netid]; free(n->name); free(n->symb); free(n->host); @@ -552,53 +573,25 @@ net_del_raw(int index) } void -net_resize(void) -{ - Htiter it = {0}; - - /* resize networks array */ - if ((networks = realloc(networks, ((size_t)netcap + NET_ADDEND) * sizeof(Network))) == NULL) - goto error; - memset(networks + netcap, 0, NET_ADDEND * sizeof(Network)); - /* also resize all the user's array */ - while (htiterate(users, &it)) { - if ((it.node->val = realloc(it.node->val, ((size_t)netcap + NET_ADDEND) * sizeof(int))) == NULL) - goto error; - memset((int *)it.node->val + netcap, 0, NET_ADDEND * sizeof(int)); - } - netcap += NET_ADDEND; - return; -error: - printf("error: realloc: %s\n", strerror(errno)); - terminate(1); -} - -void -net_update(void) +net_update(int netid) { int *fds; Htiter it = {0}; while (htiterate(users, &it)) { fds = (int *)it.node->val; - /* netlen-1 can be occupied if networks are added quickly - * and has not enough time to connect to it's server */ - if (fds[netlen-1] == 0) { - fds[netlen-1] = clone_add(netlen-1, it.node->key); - } + fds[netid] = clone_add(netid, it.node->key); } } void -user_add(char *unick, int nfd) +user_add(char *unick, int netid) { - int i, *fds; - Network *n; - size_t len; - char *nick; + int i, *fds; + size_t len; + char *nick; - n = &networks[fdnet[nfd]]; - len = strlen(unick) + strlen(n->symb) + 2 + 1; + len = strlen(unick) + strlen(networks[netid].symb) + 2 + 1; /* too long nick */ if (len-1 > MAX_NICK_LEN) { @@ -610,15 +603,18 @@ user_add(char *unick, int nfd) 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)); fds = ecalloc((size_t)netcap, sizeof(int)); - sprintf(nick, "%s[%s]", unick, n->symb); + sprintf(nick, "%s[%s]", unick, networks[netid].symb); /* clone the user on all other network */ for (i = 0; i < netlen; i++) { - /* set -1 on user's network */ - if (i == fdnet[nfd]) { + if (networks[i].join == 0) + continue; + if (i == netid) { fds[i] = -1; } else { fds[i] = clone_add(i, nick); @@ -637,87 +633,97 @@ user_del(char *nick, char *msg) { int *fds, i; if ((fds = htsearch(users, nick)) == NULL) { - printf("error: user '%s' doesn't exits\n", nick); + printf("error: user '%s' doesn't exists\n", nick); return; } for (i = 0; i < netlen; i++) { - if (fds[i] > 0) - fd_del(fds[i], msg); + if (fds[i] > 0) { + writeall(fds[i], msg); + fd_del(fds[i]); + } } htremove(users, nick); } int * -user_fds(char *nick, int nfd) +user_fds(char *nick, int netid) { - unsigned int suf; + unsigned int suffix; int *fds = NULL; /* count suffix */ - for (suf = 0; nick[strlen(nick)-suf-1] == '_'; suf++); + for (suffix = 0; nick[strlen(nick)-suffix-1] == '_'; suffix++); /* remove suffix */ - if (suf > 0) - nick[strlen(nick)-suf] = '\0'; + if (suffix > 0) + nick[strlen(nick)-suffix] = '\0'; fds = htsearch(users, nick); /* if match but suffix doesn't match */ - if ((fds != NULL) && (fdsuf[fds[fdnet[nfd]]] != (int)suf)) + if ((fds != NULL) && (fddata[fdtoid[fds[netid]]].suffix != (int)suffix)) fds = NULL; - /* add back suffix */ - if (suf > 0) + /* add suffix back */ + if (suffix > 0) nick[strlen(nick)] = '_'; return fds; } int -clone_add(int netindex, char *nick) +clone_add(int netid, char *nick) { - int fd; - Network *n = &networks[netindex]; + int fd; + Network *n = &networks[netid]; - if ((fd = fd_add(n->host, n->port)) == -1) + if ((fd = dial(n->host, n->port)) == -1) return -1; + fd_add(fd, netid, CLONE_FD, 0); /* send NICK and USER commands */ - fdprintf(fd, "NICK %s\r\n", nick); - fdprintf(fd, "USER %s 0 * :%s\r\n", n->name, n->name); - /* add a user */ - fdnet[fd] = netindex; - fdtype[fd] = FD_TYPE_CLONE; - fdsuf[fd] = 0; + snprintf(msg, sizeof(msg), "NICK %s\r\n", nick); + writeall(fd, msg); + snprintf(msg, sizeof(msg), "USER user 0 * :user\r\n"); + writeall(fd, msg); return fd; } -int -fd_add(char *host, char *port) +void +fd_add(int fd, int netid, int type, int suffix) { - int fd; - if ((fd = dial(host, port)) == -1) - return -1; - - FD_SET(fd, &fdset); - if (fd+1 > nfds) - nfds = fd+1; - - return fd; + /* resize if full */ + if (fd+1 == fdscap) { + fdtoid = erecalloc(fdtoid, (size_t)fdscap, FD_ADDEND, sizeof(int)); + fdscap += FD_ADDEND; + } + if (pfdlen == pfdcap) { + pfdset = erecalloc(pfdset, (size_t)pfdcap, POLLFD_ADDEND, sizeof(struct pollfd)); + fddata = erecalloc(fddata, (size_t)pfdcap, POLLFD_ADDEND, sizeof(struct fdData)); + pfdcap += POLLFD_ADDEND; + } + pfdset[pfdlen].fd = fd; + pfdset[pfdlen].events = POLLIN; + fddata[pfdlen].netid = netid; + fddata[pfdlen].type = type; + fddata[pfdlen].suffix = suffix; + fdtoid[fd] = pfdlen; + pfdlen++; } void -fd_del(int fd, char *msg) +fd_del(int fd) { - fdprintf(fd, "QUIT :%s\r\n", msg); close(fd); - FD_CLR(fd, &fdset); - fdnet[fd] = -1; + pfdset[fdtoid[fd]] = pfdset[pfdlen-1]; + fddata[fdtoid[fd]] = fddata[pfdlen-1]; + fdtoid[fd] = pfdlen-1; + pfdlen--; } void -nick_add_symb(char *nick, int fd) +nick_add_symb(char *nick, int netid) { strcat(nick, "["); - strcat(nick, networks[fdnet[fd]].symb); + strcat(nick, networks[netid].symb); strcat(nick, "]"); } @@ -726,7 +732,7 @@ nick_add_symb(char *nick, int fd) * src will be destructed */ void -privmsg_update(char *dst, char *src, int fd) +privmsg_update(char *dst, char *src, int netid) { char d; /* delimiter */ char *n; @@ -742,7 +748,7 @@ privmsg_update(char *dst, char *src, int fd) } /* check if the word is nick */ - if (user_fds(src, fd) != NULL) + if (user_fds(src, netid) != NULL) *strrchr(src, '[') = '\0'; strcat(dst, src); @@ -756,11 +762,11 @@ void terminate(int status) { int i; - for (i = 0; i < nfds; i++) { - if (fdnet[i] >= 0) { - /* printf("%d: shutting down\n", i); */ - fd_del(i, "linker shutting down"); - } + snprintf(msg, sizeof(msg), "QUIT :linker shutting down\r\n"); + for (i = 0; i < pfdlen; i++) { + if (fddata[i].type != FIFO_FD) + writeall(pfdset[i].fd, msg); + fd_del(pfdset[i].fd); } /* delete all the users */ htdestroy(users); @@ -768,13 +774,19 @@ terminate(int status) /* delete all the networks */ for (i = 0; i < netlen; i++) net_del_raw(i); - free(networks); - if (status == 0) + + free(pfdset); + free(fddata); + free(fdtoid); + + if (status == 0) { printf("exit successfully\n"); - else + exit(0); + } else { printf("aborted\n"); - exit(1); + exit(1); + } } void @@ -806,8 +818,8 @@ print_table(void) for (i = 0; i < netlen; i++) { printf("%d", fds[i]); /* print suffix */ - if ((fds[i] > 0) && (fdsuf[fds[i]] > 0)) - printf("(%d)", fdsuf[fds[i]]); + if ((fds[i] > 0) && (fddata[fdtoid[fds[i]]].suffix > 0)) + printf("(%d)", fddata[fdtoid[fds[i]]].suffix); printf("\t"); } printf("\n"); diff --git a/util.c b/util.c @@ -4,27 +4,53 @@ */ #include <ctype.h> +#include <errno.h> +#include <fcntl.h> #include <stdarg.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> -#include <errno.h> + +/* + * ecalloc - calloc with error handling + */ void * ecalloc(size_t nmemb, size_t size) { void *p; if ((p = calloc(nmemb, size)) == NULL) { printf("error: calloc: %s\n", strerror(errno)); - return NULL; + exit(1); } return p; } +/* + * erecalloc -- allocate more memory, zero new memory, handle error + * ptr - pointer of the old memory + * omemb - no. of member of old pointer + * nmemb - no. of member to extend + * size - size of one member + */ +void * +erecalloc(void *ptr, size_t omemb, size_t nmemb, size_t size) +{ + 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); */ + return p; +} + char* split(char **str, char ch) { @@ -43,27 +69,46 @@ split(char **str, char ch) return token; } -ssize_t -fdprintf(int fd, const char *fmt, ...) +int +fifo_open(char *path) { - va_list args; - char buf[1024]; - size_t len; - ssize_t n, i = 0; + struct stat st; + int fd; + + /* 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; + } - va_start(args, fmt); - vsnprintf(buf, sizeof(buf), fmt, args); - va_end(args); + /* open fifo */ + fd = open(path, O_RDONLY | O_NONBLOCK, 0); + if (fd == -1) + printf("error: cannot open() '%s'\n", path); + + return fd; +} - len = strlen(buf); - while (i < (ssize_t)len) { - if ((n = write(fd, buf + i, len - (size_t)i)) == -1) { +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) { printf("error: write failed: %s\n", strerror(errno)); return -1; } - i += n; + sent += n; + left -= n; } - return i; + return sent; } int @@ -83,7 +128,7 @@ dial(char *host, char *port) for (res = res0; res != NULL; res = res->ai_next) { if ((fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0) continue; - if (connect(fd, res->ai_addr, res->ai_addrlen) != 0) { + if (connect(fd, res->ai_addr, res->ai_addrlen) == -1) { close(fd); fd = -1; continue;