wm: ticl

Download patch

ref: e09ff804102de75826409b6043619d2668d9cef8
parent: 99aaa335c1a504b5210fd9e354a49749cec5c88b
author: libredev <libredev@ircforever.org>
date: Sun Nov 20 17:37:18 EST 2022

optimized and added timeout

--- a/main.c
+++ b/main.c
@@ -9,6 +9,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <poll.h>
+#include <time.h>
 #include <unistd.h>
 
 #include "htable.h"
@@ -17,17 +18,14 @@
 #define BUFFER_LEN	1024
 #define MAX_NICK_LEN	16
 
-#define FD_ADDEND	100
 #define POLLFD_ADDEND	100
 #define NET_ADDEND	10
 #define USER_ADDEND	100
 
-#define FIFO_FD		1
-#define LINKER_FD	2
-#define CLONE_FD	3
+#define TIME_OUT	180
 
 typedef struct {
-	int	 fd;	/* fd of linker	*/
+	int	 id;	/* fd of linker	*/
 	int	 join;	/* is joined	*/
 	char	*name;	/* name		*/
 	char	*symb;	/* symbol	*/
@@ -37,18 +35,17 @@
 } Network;
 
 /* user defined data for each fd */
-struct fdData {
-	int	netid;		/* net index		*/
-	int	type;		/* fd type		*/
-	int	suffix;		/* fd suffix count	*/
+struct FdData {
+	int	 netid;		/* net index	*/
+	int	 suffix;	/* suffix count	*/
+	char	*user;		/* user nick	*/
+	time_t	 time;
 };
 
-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 struct pollfd	*fdset;		/* pollfd set		*/
+static struct FdData	*fddata;	/* user data set	*/
+static int		 fdslen;	/* fdset length	*/
+static int		 fdscap;	/* fdset capacity	*/
 
 static Network	*networks;	/* linked list of networks	*/
 static int	 netlen;	/* current network length	*/
@@ -67,10 +64,11 @@
 void	 net_update(int);
 void	 user_add(char *, int);
 void	 user_del(char *, char *);
-int	*user_fds(char *, int);
-int	 clone_add(int, char *);
-void	 fd_add(int, int, int, int);
-void	 fd_del(int);
+int	*user_ids(char *, int);
+int	*euserids(char *);
+int	 clone_add(char *, int);
+int	 fdset_add(int, int, int, char *);
+void	 fdset_del(int);
 void   	 nick_add_symb(char *, int);
 void   	 privmsg_update(char *, char *, int);
 void   	 terminate(int);
@@ -82,7 +80,8 @@
 int
 main(int argc, char *argv[])
 {
-	int i, n;
+	int i, n, timeout_id;
+	time_t last_time, timeout;
 
 	/* set stdout to unbufferd */
 	setvbuf(stdout, NULL, _IONBF, 0);
@@ -94,11 +93,9 @@
 	}
 
 	/* 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));
+	fdscap = POLLFD_ADDEND;
+	fdset = ecalloc((size_t)fdscap, sizeof(struct pollfd));
+	fddata = ecalloc((size_t)fdscap, sizeof(struct FdData));
 	netcap = NET_ADDEND;
 	networks = ecalloc((size_t)netcap, sizeof(Network));
 	/*
@@ -110,22 +107,44 @@
 	users = htcreate((KeyLenFn *)strlen, (KeyCmpFn *)strcmp, free, free, USER_ADDEND);
 
 	/* add fifo_fd */
-	fd_add(fifo_open(argv[1]), 0, FIFO_FD, 0);
+	fdset_add(fifo_open(argv[1]), -1, -1, NULL);
 
 	/* select loop */
 	while (isrunning) {
-		if ((n = poll(pfdset, (nfds_t)pfdlen, -1)) == -1) {
+		/* calculate timeout from fdset */
+		last_time = time(NULL);
+		for (i = 0; i < fdslen; i++) {
+			if ((fddata[i].netid != -1)	/* ignore fifo */
+			&& (fddata[i].time < last_time)) {
+				last_time = fddata[i].time;
+				timeout_id = i;
+			}
+		}
+		timeout = TIME_OUT - (time(NULL) - last_time);
+		if (timeout < 0)
+			timeout = 0;
+
+		n = poll(fdset, (nfds_t)fdslen, (int)timeout * 1000);
+		if (n == -1) {
 			printf("error: poll: %s\n", strerror(errno));
 			terminate(1);
+		} else if (n == 0) {
+			snprintf(msg, sizeof(msg), "PING :%s\r\n",
+				networks[fddata[timeout_id].netid].host);
+			writeall(fdset[timeout_id].fd, msg);
+			fddata[timeout_id].time = time(NULL);
+			continue;
 		}
-		for (i = 0; i < pfdlen; i++) {
-			if (!(pfdset[i].revents & (POLLIN|POLLHUP)))
+		for (i = 0; i < fdslen; i++) {
+			if (!(fdset[i].revents & (POLLIN|POLLHUP)))
 				continue;
-			/* printf("poll: %d\n", pfdset[i].fd); */
-			if (fddata[i].type == FIFO_FD)
+			if (fddata[i].netid == -1)
 				handle_fifo_input(i, argv[1]);
 			else
 				handle_server_output(i);
+
+			fddata[i].time = time(NULL);
+
 			/*
 			 * handle one ready fd at one time because if we
 			 * close upcoming ready fd, it cause infinite loop.
@@ -146,12 +165,12 @@
 	ssize_t n;
 
 	/* if failed to read data */
-	if ((n = readline(pfdset[id].fd, buffer, sizeof(buffer))) < 1) {
+	if ((n = readline(fdset[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);
+			fdset_del(id);
+			fdset_add(fifo_open(path), -1, -1, NULL);
 		} else if ((errno != EAGAIN) && (errno != EINTR)) {
-			printf("error: read: %s\n", strerror(errno));
+			printf("error: %d: read: %s\n", fdset[id].fd, strerror(errno));
 		}
 		return;
 	}
@@ -201,13 +220,15 @@
 	ssize_t n;
 
 	/* if failed to read data */
-	if ((n = readline(pfdset[id].fd, buffer, sizeof(buffer))) < 1) {
+	if ((n = readline(fdset[id].fd, buffer, sizeof(buffer))) < 1) {
 		if (n == 0) {
-			printf("error: remote host closed connection: %s\n", strerror(errno));
+			printf("error: %d: connection closed\n", fdset[id].fd);
+			printf("ping: %d : %ld\n", fdset[id].fd, time(0) - fddata[id].time);
 			terminate(1);
-			/* fd_del(pfdset[id].fd); */
+			/* fdset_del(fdset[id].fd); */
 		} else if ((errno != EAGAIN) && (errno != EINTR)) {
-			printf("error: read: %s\n", strerror(errno));
+			printf("error: %d: read: %s\n", fdset[id].fd, strerror(errno));
+			printf("ping: %d : %ld\n", fdset[id].fd, time(0) - fddata[id].time);
 			terminate(1);
 		}
 		return;
@@ -239,7 +260,7 @@
 		goto printbuffer;
 	} else if (strcmp(cmd, "PING") == 0) {
 		snprintf(msg, sizeof(msg), "PONG %s\r\n", buf);
-		writeall(pfdset[id].fd, msg);
+		writeall(fdset[id].fd, msg);
 		return;
 	}
 	/* strip nick from first column */
@@ -287,28 +308,28 @@
 			strcat(nick, "_");
 			fddata[id].suffix++;
 			snprintf(msg, sizeof(msg), "NICK %s\r\n", nick);
-			writeall(pfdset[id].fd, msg);
+			writeall(fdset[id].fd, msg);
 		}
 		return;
 	} else if (strcmp(cmd, "001") == 0) {
 		snprintf(msg, sizeof(msg), "JOIN %s\r\n", networks[netid].chan);
-		writeall(pfdset[id].fd, msg);
+		writeall(fdset[id].fd, msg);
 		return;
 	} else if (strcmp(cmd, "PRIVMSG") == 0) {
 		char privmsg[BUFFER_LEN] = "";
-		int *fds;
+		int *ids;
 
-		if (fddata[id].type == LINKER_FD) {
+		if (fddata[id].user == NULL) {	/* if linker */
 			nick_add_symb(nick, netid);
-			if ((fds = htsearch(users, nick)) == NULL)
+			if ((ids = htsearch(users, nick)) == NULL)
 				return;
 
 			split(&buf, ':');	/* set buf to msg */
 			privmsg_update(privmsg, buf, netid);
 			for (i = 0; i < netlen; i++) {
-				if (fds[i] > 0) {
+				if (ids[i] > 0) {
 					snprintf(msg, sizeof(msg), "PRIVMSG %s :%s\r\n", networks[i].chan, privmsg);
-					writeall(fds[i], msg);
+					writeall(fdset[ids[i]].fd, msg);
 				}
 			}
 		} else {
@@ -320,7 +341,7 @@
 				return;
 
 			nick_add_symb(nick, netid);
-			if ((fds = htsearch(users, nick)) == NULL)
+			if ((ids = htsearch(users, nick)) == NULL)
 				return;
 
 			/* split user nick and network symbol */
@@ -337,12 +358,12 @@
 			split(&buf, ':');	/* set buf to msg */
 			privmsg_update(privmsg, buf, netid);
 			snprintf(msg, sizeof(msg), "PRIVMSG %s :%s\r\n", user, privmsg);
-			writeall(fds[i], msg);
+			writeall(fdset[ids[i]].fd, msg);
 		}
 		return;
 	}
 	/* these messages are handled by linker */
-	if (fddata[id].type == CLONE_FD) {
+	if (fddata[id].user != NULL) {	/* if clone */
 		if ((strcmp(cmd, "353") == 0)
 		|| (strcmp(cmd, "JOIN") == 0)
 		|| (strcmp(cmd, "QUIT") == 0)
@@ -369,21 +390,22 @@
 		return;
 	} else if (strcmp(cmd, "JOIN") == 0) {
 		if ((strcmp(nick, linker_nick) != 0)
-		&& (user_fds(nick, netid) == NULL))	/* if not clone */
+		&& (user_ids(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, netid);
 		snprintf(msg, sizeof(msg), "QUIT :%s\r\n", buf);
-		user_del(nick, msg);
+		nick_add_symb(nick, netid);
+		if (htsearch(users, nick) != NULL)
+			user_del(nick, msg);
 		return;
 	} else if (strcmp(cmd, "NICK") == 0) {
-		int *fds, i;
+		int *ids, i;
 		char *newnick;
 
 		nick_add_symb(nick, netid);
-		if ((fds = htsearch(users, nick)) == NULL)
+		if ((ids = htsearch(users, nick)) == NULL)
 			return;
 
 		/* set buf to new nick */
@@ -395,13 +417,13 @@
 
 		snprintf(msg, sizeof(msg), "NICK %s\r\n", newnick);
 		for (i = 0; i < netlen; i++) {
-			if (fds[i] > 0)
-				writeall(fds[i], msg);
+			if (ids[i] > 0)
+				writeall(fdset[ids[i]].fd, msg);
 		}
 		return;
 	} else if (strcmp(cmd, "KICK") == 0) {
 		/* :<nick_which_is_kicking>!~user@host KICK <channel> <nick_which_has_been_kicked> :<kick_msg> */
-		int *fds;
+		int *ids;
 		char *chan = split(&buf, ' ');
 		char *user = split(&buf, ' ');
 
@@ -415,7 +437,7 @@
 		}
 
 		/* delete the user if the message from the same network */
-		if ((fds = user_fds(user, netid)) == NULL) {
+		if ((ids = user_ids(user, netid)) == NULL) {
 			nick_add_symb(user, netid);
 			user_del(user, msg);
 			return;
@@ -422,9 +444,8 @@
 		}
 
 		/* close the kicked fd */
-		writeall(fds[netid], msg);
-		fd_del(fds[netid]);
-		fds[netid] = -2;
+		writeall(fdset[ids[netid]].fd, msg);
+		fdset_del(id);
 
 		/*
 		 * send notice in the channel through linker
@@ -431,7 +452,7 @@
 		 */
 		/* get the original user netid */
 		for (i = 0; i < netlen; i++) {
-			if (fds[i] == -1)
+			if (ids[i] == -1)
 				break;
 		}
 		/* set buf to msg */
@@ -442,11 +463,11 @@
 		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);
+		writeall(fdset[networks[i].id].fd, msg);
 		return;
 	}
 printbuffer:
-	printf("%d: %s\n", pfdset[id].fd, backup);
+	printf("%d: %s\n", fdset[id].fd, backup);
 }
 
 void
@@ -485,7 +506,6 @@
 	/* connect */
 	if ((fd = dial(host, port)) == -1)
 		return;
-	fd_add(fd, netlen, LINKER_FD, 0);
 	/* send NICK and USER commands */
 	snprintf(msg, sizeof(msg), "NICK linker\r\n");
 	writeall(fd, msg);
@@ -493,7 +513,7 @@
 	writeall(fd, msg);
 	/* add a network */
 	n = &networks[netlen];
-	n->fd = fd;
+	n->id = fdset_add(fd, netlen, 0, NULL);
 	n->join = 0;
 	n->name = strdup(name);
 	n->symb = strdup(symb);
@@ -507,7 +527,7 @@
 void
 net_del(char *name)
 {
-	int	i, netid, *fds;
+	int	i, netid, *ids;
 
 	Htiter	it	= {0};	/* current iterator */
 	Htiter	lastit	= {0};	/* last iterator */
@@ -530,34 +550,34 @@
 
 	/* reconstruct the user-clones table */
 	while (htiterate(users, &it)) {
-		fds = (int *)it.node->val;
+		ids = (int *)it.node->val;
 		/* delete all the users of deleting network */
-		if (fds[netid] == -1) {
+		if (ids[netid] == -1) {
 			user_del(it.node->key, msg);
 			/* this node is deleted */
 			it = lastit;
 		/* delete the clones */
 		} else {
-			if (fds[netid] > 0) {
-				writeall(fds[netid], msg);
-				fd_del(fds[netid]);
+			if (ids[netid] > 0) {
+				writeall(fdset[ids[netid]].fd, msg);
+				fdset_del(ids[netid]);
 			}
 			/* swap last with current one */
-			fds[netid] = fds[netlen-1];
+			ids[netid] = ids[netlen-1];
 		}
 		lastit = it;
 	}
 
-	/* set pollfds netid with last netid to current netid. */
-	for (i = 0; i < pfdlen; i++) {
+	/* set netid of fds with last netid to current netid. */
+	for (i = 0; i < fdslen; i++) {
 		if (fddata[i].netid == netlen-1)
 			fddata[i].netid = netid;
 	}
 
-	writeall(networks[netid].fd, msg);
-	fd_del(networks[netid].fd);
+	writeall(fdset[networks[netid].id].fd, msg);
+	fdset_del(networks[netid].id);
 	net_del_raw(netid);
-	printf("%d: network '%s' deleted\n", networks[netid].fd, name);
+	printf("%d: network '%s' deleted\n", fdset[networks[netid].id].fd, name);
 	/* swap the network with the last */
 	networks[netid] = networks[netlen-1];
 	netlen--;
@@ -577,19 +597,15 @@
 void
 net_update(int netid)
 {
-	int	*fds;
-	Htiter	 it = {0};
-
-	while (htiterate(users, &it)) {
-		fds = (int *)it.node->val;
-		fds[netid] = clone_add(netid, it.node->key);
-	}
+	Htiter it = {0};
+	while (htiterate(users, &it))
+		((int *)it.node->val)[netid] = clone_add(it.node->key, netid);
 }
 
 void
 user_add(char *unick, int netid)
 {
-	int	i, *fds;
+	int	i, *ids;
 	size_t	len;
 	char	*nick;
 
@@ -609,7 +625,7 @@
 
 	/* allocate a new user */
 	nick = ecalloc(len, sizeof(char));
-	fds = ecalloc((size_t)netcap, sizeof(int));
+	ids = ecalloc((size_t)netcap, sizeof(int));
 	sprintf(nick, "%s[%s]", unick, networks[netid].symb);
 
 	/* clone the user on all other network */
@@ -617,13 +633,13 @@
 		if (networks[i].join == 0)
 			continue;
 		if (i == netid) {
-			fds[i] = -1;
+			ids[i] = -1;
 		} else {
-			fds[i] = clone_add(i, nick);
+			ids[i] = clone_add(nick, i);
 		}
 	}
 	/* insert it to the users hash table */
-	if (htinsert(users, nick, fds) == -1) {
+	if (htinsert(users, nick, ids) == -1) {
 		/* this shouldn't happen as it was already checked */
 		printf("error: user '%s' already exists\n", nick);
 		terminate(1);
@@ -633,16 +649,12 @@
 void
 user_del(char *nick, char *msg)
 {
-	int *fds, i;
-	if ((fds = htsearch(users, nick)) == NULL) {
-		printf("error: user '%s' doesn't exists\n", nick);
-		return;
-	}
-
+	int i;
+	int *ids = euserids(nick);
 	for (i = 0; i < netlen; i++) {
-		if (fds[i] > 0) {
-			writeall(fds[i], msg);
-			fd_del(fds[i]);
+		if (ids[i] > 0) {
+			writeall(fdset[ids[i]].fd, msg);
+			fdset_del(ids[i]);
 		}
 	}
 	htremove(users, nick);
@@ -649,10 +661,10 @@
 }
 
 int *
-user_fds(char *nick, int netid)
+user_ids(char *nick, int netid)
 {
 	unsigned int suffix;
-	int *fds = NULL;
+	int *ids = NULL;
 
 	/* count suffix */
 	for (suffix = 0; nick[strlen(nick)-suffix-1] == '_'; suffix++);
@@ -660,65 +672,80 @@
 	if (suffix > 0)
 		nick[strlen(nick)-suffix] = '\0';
 
-	fds = htsearch(users, nick);
+	ids = htsearch(users, nick);
 	/* if match but suffix doesn't match */
-	if ((fds != NULL) && (fddata[fdtoid[fds[netid]]].suffix != (int)suffix))
-		fds = NULL;
+	if ((ids != NULL) && (fddata[ids[netid]].suffix != (int)suffix))
+		ids = NULL;
 
 	/* add suffix back */
 	if (suffix > 0)
 		nick[strlen(nick)] = '_';
 
-	return fds;
+	return ids;
 }
 
+/* get user ids */
+int *
+euserids(char *user)
+{
+	int *ids;
+	if ((ids = htsearch(users, user)) == NULL) {
+		printf("error: cannot find user '%s'\n", user);
+		terminate(1);
+	}
+	return ids;
+}
+
 int
-clone_add(int netid, char *nick)
+clone_add(char *nick, int netid)
 {
 	int	fd;
 	Network	*n = &networks[netid];
-
 	if ((fd = dial(n->host, n->port)) == -1)
 		return -1;
-	fd_add(fd, netid, CLONE_FD, 0);
 	/* send NICK and USER commands */
 	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;
+	return fdset_add(fd, netid, 0, nick);
 }
 
-void
-fd_add(int fd, int netid, int type, int suffix)
+int
+fdset_add(int fd, int netid, int suffix, char *user)
 {
-	/* resize if full */
-	if (fd+1 == fdscap) {
-		fdtoid = erecalloc(fdtoid, (size_t)fdscap, FD_ADDEND, sizeof(int));
-		fdscap += FD_ADDEND;
+	if (fdslen == fdscap) {
+		fdset = erecalloc(fdset, (size_t)fdscap, POLLFD_ADDEND, sizeof(struct pollfd));
+		fddata = erecalloc(fddata, (size_t)fdscap, POLLFD_ADDEND, sizeof(struct FdData));
+		fdscap += POLLFD_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++;
+	fdset[fdslen].fd = fd;
+	fdset[fdslen].events = POLLIN;
+	fddata[fdslen].netid = netid;
+	fddata[fdslen].suffix = suffix;
+	fddata[fdslen].user = user;
+	fddata[fdslen].time = time(NULL);
+	return fdslen++;
 }
 
 void
-fd_del(int fd)
+fdset_del(int id)
 {
-	close(fd);
-	pfdset[fdtoid[fd]] = pfdset[pfdlen-1];
-	fddata[fdtoid[fd]] = fddata[pfdlen-1];
-	fdtoid[fd] = pfdlen-1;
-	pfdlen--;
+	/* set id of last fd to current id */
+	if (fddata[fdslen-1].user != NULL)
+		euserids(fddata[fdslen-1].user)[fddata[fdslen-1].netid] = id;
+	else if ((fddata[fdslen-1].user == NULL) && (fddata[fdslen-1].netid != -1))
+		networks[fddata[fdslen-1].netid].id = id;
+	/* set id of removing fd to -2 */
+	if (fddata[id].user != NULL)
+		euserids(fddata[id].user)[fddata[id].netid] = -2;
+	else if ((fddata[id].user == NULL) && (fddata[id].netid != -1))
+		networks[fddata[id].netid].id = -2;
+
+	close(fdset[id].fd);
+	fdset[id] = fdset[fdslen-1];
+	fddata[id] = fddata[fdslen-1];
+	fdslen--;
 }
 
 void
@@ -750,7 +777,7 @@
 		}
 
 		/* check if the word is nick */
-		if (user_fds(src, netid) != NULL)
+		if (user_ids(src, netid) != NULL)
 			*strrchr(src, '[') = '\0';
 
 		strcat(dst, src);
@@ -765,10 +792,10 @@
 {
 	int i;
 	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);
+	for (i = 0; i < fdslen; i++) {
+		if (fddata[i].netid != -1)
+			writeall(fdset[i].fd, msg);
+		fdset_del(i);
 	}
 	/* delete all the users */
 	htdestroy(users);
@@ -776,11 +803,10 @@
 	/* delete all the networks */
 	for (i = 0; i < netlen; i++)
 		net_del_raw(i);
-	free(networks);
 
-	free(pfdset);
+	free(networks);
+	free(fdset);
 	free(fddata);
-	free(fdtoid);
 
 	if (status == 0) {
 		printf("exit successfully\n");
@@ -794,7 +820,7 @@
 void
 print_table(void)
 {
-	int	i, *fds, diff, tabs;
+	int	i, *ids, diff, tabs;
 	Htiter	it = {0};
 	char	*nick;
 
@@ -805,11 +831,11 @@
 	/* print networks */
 	printf("Networks\t\t");
 	for (i = 0; i < netlen; i++)
-		printf("%s(%d)\t", networks[i].symb, networks[i].fd);
+		printf("%s(%d)\t", networks[i].symb, fdset[networks[i].id].fd);
 	printf("\n");
 
 	while (htiterate(users, &it)) {
-		fds  = (int *)it.node->val;
+		ids  = (int *)it.node->val;
 		nick = (char *)it.node->key;
 		/* print tabbed user nick */
 		printf("%s", nick);
@@ -816,12 +842,12 @@
 		diff = 24 - (int)strlen(nick);
 		tabs = ((diff / 8) + (diff % 8 > 0));
 		printf("%.*s", tabs, "\t\t\t");
-		/* print tabbed clones fds */
+		/* print tabbed clones ids */
 		for (i = 0; i < netlen; i++) {
-			printf("%d", fds[i]);
+			printf("%d", ids[i]);
 			/* print suffix */
-			if ((fds[i] > 0) && (fddata[fdtoid[fds[i]]].suffix > 0))
-				printf("(%d)", fddata[fdtoid[fds[i]]].suffix);
+			if ((ids[i] > 0) && (fddata[ids[i]].suffix > 0))
+				printf("(%d)", fddata[ids[i]].suffix);
 			printf("\t");
 		}
 		printf("\n");
--- a/util.c
+++ b/util.c
@@ -112,6 +112,27 @@
 }
 
 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;
@@ -126,14 +147,19 @@
 		return -1;
 	}
 	for (res = res0; res != NULL; res = res->ai_next) {
-		if ((fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0)
+		if ((fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) == -1)
 			continue;
-		if (connect(fd, res->ai_addr, res->ai_addrlen) == -1) {
-			close(fd);
-			fd = -1;
+		if (fcntl(fd, F_SETFL, O_NONBLOCK) == -1) {
+			printf("error: fcntl: %s\n", strerror(errno));
 			continue;
 		}
-		break;
+		if (connect(fd, res->ai_addr, res->ai_addrlen) == 0)
+			break;
+		else if ((errno == EINPROGRESS) && (connect_wait(fd) == 0))
+			break;
+		printf("error: connect: %s\n", strerror(errno));
+		close(fd);
+		fd = -1;
 	}
 	if (fd == -1)
 		printf("error: cannot connect to host '%s'\n", host);