ticl

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

commit e09ff804102de75826409b6043619d2668d9cef8
parent 99aaa335c1a504b5210fd9e354a49749cec5c88b
Author: libredev <libredev@ircforever.org>
Date:   Sun, 20 Nov 2022 22:37:18 +0530

optimized and added timeout

Diffstat:
Mmain.c | 324+++++++++++++++++++++++++++++++++++++++++++------------------------------------
Mutil.c | 36+++++++++++++++++++++++++++++++-----
2 files changed, 206 insertions(+), 154 deletions(-)

diff --git 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 @@ typedef struct { } 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_del_raw(int); 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 @@ void print_border(void); 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 @@ main(int argc, char *argv[]) } /* 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 @@ main(int argc, char *argv[]) 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 @@ handle_fifo_input(int id, char *path) 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 @@ handle_server_output(int id) 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 @@ handle_server_output(int id) 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 @@ handle_server_output(int id) 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 @@ handle_server_output(int id) 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 @@ handle_server_output(int id) split(&buf, ':'); /* set buf to msg */ privmsg_update(privmsg, buf, netid); snprintf(msg, sizeof(msg), "PRIVMSG %s :%s\r\n", user, privmsg); - writeall(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 @@ handle_server_output(int id) 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 @@ handle_server_output(int id) 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,23 +437,22 @@ handle_server_output(int id) } /* 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; } /* 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 */ /* 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 @@ handle_server_output(int id) 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 @@ net_add(char *name, char *symb, char *host, char *port, char *chan) /* 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 @@ net_add(char *name, char *symb, char *host, char *port, char *chan) 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 @@ net_add(char *name, char *symb, char *host, char *port, char *chan) 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 @@ net_del(char *name) /* 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 @@ net_del_raw(int netid) 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 @@ user_add(char *unick, int netid) /* 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 @@ user_add(char *unick, int netid) 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,26 +649,22 @@ user_add(char *unick, int netid) 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); } 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 @@ user_fds(char *nick, int netid) 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 @@ privmsg_update(char *dst, char *src, int netid) } /* 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 @@ terminate(int status) { 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 @@ terminate(int status) /* 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 @@ terminate(int status) void print_table(void) { - int i, *fds, diff, tabs; + int i, *ids, diff, tabs; Htiter it = {0}; char *nick; @@ -805,23 +831,23 @@ print_table(void) /* 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); 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"); diff --git a/util.c b/util.c @@ -112,6 +112,27 @@ 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; @@ -126,14 +147,19 @@ dial(char *host, char *port) 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);