ref: 66e66acea622b6183cdf8767c4d268bfe100bce0
parent: 8df402e53d02ca2c0b976715ce57c28750dd99d9
author: libredev <libredev@ircforever.org>
date: Sat Nov 19 18:30:10 EST 2022
fix duplicate clone and replace select() with poll()
--- 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
--- a/README
+++ b/README
@@ -70,6 +70,10 @@
$ ./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 @@
$ 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 @@
See COPYING file for more information.
-FAQ
----
+Note
+----
-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.
-
-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
--- a/main.c
+++ b/main.c
@@ -8,8 +8,7 @@
#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"
@@ -16,17 +15,20 @@
#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 @@
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 @@
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 @@
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 @@
*/
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;
+ /* add fifo_fd */
+ fd_add(fifo_open(argv[1]), 0, FIFO_FD, 0);
- /* 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;
-
/* 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));
+ if ((n = poll(pfdset, (nfds_t)pfdlen, -1)) == -1) {
+ printf("error: poll: %s\n", strerror(errno));
terminate(1);
- } else if (r == 0) {
- printf("error: select timeout\n");
- 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 @@
}
void
-handle_fifo_input(int fd)
+handle_fifo_input(int id, char *path)
{
char buffer[BUFFER_LEN];
char *buf;
char *cmd;
+ ssize_t n;
- if (readline(fd, buffer, sizeof(buffer)) < 1) {
- printf("error: %d: failed to read from fifo: %s\n", fd, strerror(errno));
- terminate(1);
+ /* 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 @@
}
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 (readline(fd, buffer, sizeof(buffer)) < 1) {
- printf("error: %d: remote host closed connection: %s\n", fd, strerror(errno));
- terminate(1);
+ /* 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;
}
+ /* 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 @@
} 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 @@
} 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 @@
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 @@
}
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 @@
} 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,24 +362,25 @@
|| *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;
@@ -379,13 +387,14 @@
/* 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 @@
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';
+ /* close the kicked fd */
+ writeall(fds[netid], msg);
+ fd_del(fds[netid]);
+ fds[netid] = -2;
- /* get the original user fd index */
+ /*
+ * 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 @@
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 @@
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 @@
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);
+ snprintf(msg, sizeof(msg), "QUIT :unlinking network %s\r\n", 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;
- }
-
+ /* 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 @@
}
void
-net_resize(void)
+net_update(int netid)
{
- 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)
-{
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 @@
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,36 +633,38 @@
{
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;
@@ -673,51 +671,59 @@
}
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 @@
* 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 @@
}
/* 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 @@
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 @@
/* 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 @@
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");
--- a/util.c
+++ b/util.c
@@ -4,6 +4,8 @@
*/
#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@@ -10,10 +12,15 @@
#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)
{
@@ -20,11 +27,30 @@
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 @@
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;
- va_start(args, fmt);
- vsnprintf(buf, sizeof(buf), fmt, args);
- va_end(args);
+ /* 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;
+ }
- len = strlen(buf);
- while (i < (ssize_t)len) {
- if ((n = write(fd, buf + i, len - (size_t)i)) == -1) {
+ /* open fifo */
+ fd = open(path, O_RDONLY | O_NONBLOCK, 0);
+ if (fd == -1)
+ printf("error: cannot open() '%s'\n", 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) {
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 @@
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;