diff --git a/CHANGES b/CHANGES index e37d601..ed3fa25 100644 --- a/CHANGES +++ b/CHANGES @@ -22,6 +22,9 @@ corrections: improved test.sh script + replaced the select() calls by poll() to cleanly fix the problems with + many file descriptors already open + ####################### V 1.6.0.1: new features: diff --git a/VERSION b/VERSION index c6a20a5..2b6e1d9 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -"1.6.0.1+ip4bind+recvfromfork+x64+execstderr+execspaces+cosmetics" +"1.6.0.1+ip4bind+recvfromfork+x64+execstderr+execspaces+cosmetics+poll" diff --git a/error.h b/error.h index 45168ed..1ef73a6 100644 --- a/error.h +++ b/error.h @@ -50,6 +50,8 @@ #define Error6(m,a1,a2,a3,a4,a5,a6) msg(E_ERROR,m,a1,a2,a3,a4,a5,a6) #define Error7(m,a1,a2,a3,a4,a5,a6,a7) msg(E_ERROR,m,a1,a2,a3,a4,a5,a6,a7) #define Error8(m,a1,a2,a3,a4,a5,a6,a7,a8) msg(E_ERROR,m,a1,a2,a3,a4,a5,a6,a7,a8) +#define Error9(m,a1,a2,a3,a4,a5,a6,a7,a8,a9) msg(E_ERROR,m,a1,a2,a3,a4,a5,a6,a7,a8,a9) +#define Error10(m,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10) msg(E_ERROR,m,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10) #else /* !(WITH_MSGLEVEL >= E_ERROR) */ #define Error(m) #define Error1(m,a1) @@ -60,6 +62,8 @@ #define Error6(m,a1,a2,a3,a4,a5,a6) #define Error7(m,a1,a2,a3,a4,a5,a6,a7) #define Error8(m,a1,a2,a3,a4,a5,a6,a7,a8) +#define Error9(m,a1,a2,a3,a4,a5,a6,a7,a8,a9) +#define Error10(m,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10) #endif /* !(WITH_MSGLEVEL <= E_ERROR) */ #if WITH_MSGLEVEL <= E_WARN diff --git a/socat.c b/socat.c index 6971c25..78eda0c 100644 --- a/socat.c +++ b/socat.c @@ -83,12 +83,16 @@ bool havelock; int main(int argc, const char *argv[]) { const char **arg1, *a; + char *mainwaitstring; char buff[10]; double rto; int i, argc0, result; struct utsname ubuf; int lockrc; + if (mainwaitstring = getenv("SOCAT_MAIN_WAIT")) { + sleep(atoi(mainwaitstring)); + } diag_set('p', strchr(argv[0], '/') ? strrchr(argv[0], '/')+1 : argv[0]); /* we must init before applying options because env settings have lower @@ -642,37 +646,32 @@ int socat(const char *address1, const char *address2) { returns >0 if child died and left data */ int childleftdata(xiofile_t *xfd) { - fd_set in, out, expt; + struct pollfd in; + int timeout = 0; /* milliseconds */ int retval; + /* have to check if a child process died before, but left read data */ if (XIO_READABLE(xfd) && (XIO_RDSTREAM(xfd)->howtoend == END_KILL || XIO_RDSTREAM(xfd)->howtoend == END_CLOSE_KILL || XIO_RDSTREAM(xfd)->howtoend == END_SHUTDOWN_KILL) && XIO_RDSTREAM(xfd)->para.exec.pid == 0) { - struct timeval time0 = { 0,0 }; - FD_ZERO(&in); FD_ZERO(&out); FD_ZERO(&expt); if (XIO_READABLE(xfd) && !(XIO_RDSTREAM(xfd)->eof >= 2 && !XIO_RDSTREAM(xfd)->ignoreeof)) { - FD_SET(XIO_GETRDFD(xfd), &in); - /*0 FD_SET(XIO_GETRDFD(xfd), &expt);*/ + in.fd = XIO_GETRDFD(xfd); + in.events = POLLIN/*|POLLRDBAND*/; + in.revents = 0; } do { - retval = Select(FD_SETSIZE, &in, &out, &expt, &time0); + retval = xiopoll(&in, 1, timeout); } while (retval < 0 && errno == EINTR); if (retval < 0) { -#if HAVE_FDS_BITS - Error5("select(%d, &0x%lx, &0x%lx, &0x%lx, {0}): %s", - FD_SETSIZE, in.fds_bits[0], out.fds_bits[0], - expt.fds_bits[0], strerror(errno)); -#else - Error5("select(%d, &0x%lx, &0x%lx, &0x%lx, {0}): %s", - FD_SETSIZE, in.__fds_bits[0], out.__fds_bits[0], - expt.__fds_bits[0], strerror(errno)); -#endif + Error4("xiopoll({%d,%0o}, 1, %d): %s", + in.fd, in.events, timeout, strerror(errno)); return -1; - } else if (retval == 0) { + } + if (retval == 0) { Info("terminated child did not leave data for us"); XIO_RDSTREAM(xfd)->eof = 2; xfd->stream.eof = 2; @@ -694,7 +693,11 @@ bool maywr2; /* sock2 can be written to, according to select() */ and their options are set/applied returns -1 on error or 0 on success */ int _socat(void) { - fd_set in, out, expt; + struct pollfd fds[4], + *fd1in = &fds[0], + *fd1out = &fds[1], + *fd2in = &fds[2], + *fd2out = &fds[3]; int retval; unsigned char *buff; ssize_t bytes1, bytes2; @@ -747,7 +750,7 @@ int _socat(void) { XIO_GETRDFD(sock2), XIO_GETWRFD(sock2)); while (XIO_RDSTREAM(sock1)->eof <= 1 || XIO_RDSTREAM(sock2)->eof <= 1) { - struct timeval timeout, *to = NULL; + int timeout; Debug6("data loop: sock1->eof=%d, sock2->eof=%d, closing=%d, wasaction=%d, total_to={"F_tv_sec"."F_tv_usec"}", XIO_RDSTREAM(sock1)->eof, XIO_RDSTREAM(sock2)->eof, @@ -783,59 +786,77 @@ int _socat(void) { if (polling) { /* there is a ignoreeof poll timeout, use it */ - timeout = socat_opts.pollintv; - to = &timeout; + timeout = 1000*socat_opts.pollintv.tv_sec + + socat_opts.pollintv.tv_usec/1000; /*!!! overflow?*/ } else if (socat_opts.total_timeout.tv_sec != 0 || socat_opts.total_timeout.tv_usec != 0) { /* there might occur a total inactivity timeout */ - timeout = socat_opts.total_timeout; - to = &timeout; + timeout = 1000*socat_opts.total_timeout.tv_sec + + socat_opts.total_timeout.tv_usec/1000; } else { - to = NULL; + timeout = -1; /* forever */ } if (closing>=1) { /* first eof already occurred, start end timer */ - timeout = socat_opts.closwait; - to = &timeout; + timeout = 1000*socat_opts.closwait.tv_sec + + socat_opts.closwait.tv_usec/1000; closing = 2; } do { int _errno; - FD_ZERO(&in); FD_ZERO(&out); FD_ZERO(&expt); childleftdata(sock1); childleftdata(sock2); if (closing>=1) { /* first eof already occurred, start end timer */ - timeout = socat_opts.closwait; - to = &timeout; + timeout = 1000*socat_opts.closwait.tv_sec + + socat_opts.closwait.tv_usec/1000; closing = 2; } + /* now the fds are assigned */ if (XIO_READABLE(sock1) && !(XIO_RDSTREAM(sock1)->eof > 1 && !XIO_RDSTREAM(sock1)->ignoreeof) && !socat_opts.righttoleft) { if (!mayrd1) { - FD_SET(XIO_GETRDFD(sock1), &in); + fd1in->fd = XIO_GETRDFD(sock1); + fd1in->events = POLLIN; + } else { + fd1in->fd = -1; } if (!maywr2) { - FD_SET(XIO_GETWRFD(sock2), &out); + fd2out->fd = XIO_GETWRFD(sock2); + fd2out->events = POLLOUT; + } else { + fd2out->fd = -1; } + } else { + fd1in->fd = -1; + fd2out->fd = -1; } if (XIO_READABLE(sock2) && !(XIO_RDSTREAM(sock2)->eof > 1 && !XIO_RDSTREAM(sock2)->ignoreeof) && !socat_opts.lefttoright) { if (!mayrd2) { - FD_SET(XIO_GETRDFD(sock2), &in); + fd2in->fd = XIO_GETRDFD(sock2); + fd2in->events = POLLIN; + } else { + fd2in->fd = -1; } if (!maywr1) { - FD_SET(XIO_GETWRFD(sock1), &out); + fd1out->fd = XIO_GETWRFD(sock1); + fd1out->events = POLLOUT; + } else { + fd1out->fd = -1; } + } else { + fd1out->fd = -1; + fd2in->fd = -1; } - retval = Select(FD_SETSIZE, &in, &out, &expt, to); + retval = xiopoll(fds, 4, timeout); _errno = errno; if (retval < 0 && errno == EINTR) { Info1("select(): %s", strerror(errno)); @@ -849,17 +870,10 @@ int _socat(void) { */ if (retval < 0) { -#if HAVE_FDS_BITS - Error7("select(%d, &0x%lx, &0x%lx, &0x%lx, %s%lu): %s", - FD_SETSIZE, in.fds_bits[0], out.fds_bits[0], - expt.fds_bits[0], to?"&":"NULL/", to?to->tv_sec:0, - strerror(errno)); -#else - Error7("select(%d, &0x%lx, &0x%lx, &0x%lx, %s%lu): %s", - FD_SETSIZE, in.__fds_bits[0], out.__fds_bits[0], - expt.__fds_bits[0], to?"&":"NULL/", to?to->tv_sec:0, - strerror(errno)); -#endif + Error10("xiopoll({%d,%0o}{%d,%0o}{%d,%0o}{%d,%0o}, 4, %d): %s", + fds[0].fd, fds[0].events, fds[1].fd, fds[1].events, + fds[2].fd, fds[2].events, fds[3].fd, fds[3].events, + timeout, strerror(errno)); return -1; } else if (retval == 0) { Info2("select timed out (no data within %ld.%06ld seconds)", @@ -884,17 +898,17 @@ int _socat(void) { } if (XIO_READABLE(sock1) && XIO_GETRDFD(sock1) >= 0 && - FD_ISSET(XIO_GETRDFD(sock1), &in)) { + (fd1in->revents&(POLLIN|POLLHUP|POLLERR))) { mayrd1 = true; } if (XIO_READABLE(sock2) && XIO_GETRDFD(sock2) >= 0 && - FD_ISSET(XIO_GETRDFD(sock2), &in)) { + (fd2in->revents&(POLLIN|POLLHUP|POLLERR))) { mayrd2 = true; } - if (XIO_GETWRFD(sock1) >= 0 && FD_ISSET(XIO_GETWRFD(sock1), &out)) { + if (XIO_GETWRFD(sock1) >= 0 && (fd1out->revents&(POLLOUT|POLLERR))) { maywr1 = true; } - if (XIO_GETWRFD(sock2) >= 0 && FD_ISSET(XIO_GETWRFD(sock2), &out)) { + if (XIO_GETWRFD(sock2) >= 0 && (fd2out->revents&(POLLOUT|POLLERR))) { maywr2 = true; } diff --git a/sycls.c b/sycls.c index 8581436..fee5bca 100644 --- a/sycls.c +++ b/sycls.c @@ -677,9 +677,21 @@ int Chmod(const char *path, mode_t mode) { /* we only show the first struct pollfd; hope this is enough for most cases. */ int Poll(struct pollfd *ufds, unsigned int nfds, int timeout) { int result; - Debug4("poll({%d, 0x%02hx, }, %u, %d)", ufds[0].fd, ufds[0].events, nfds, timeout); + if (nfds == 4) { + Debug10("poll({%d,0x%02hx,}{%d,0x%02hx,}{%d,0x%02hx,}{%d,0x%02hx,}, , %u, %d)", + ufds[0].fd, ufds[0].events, ufds[1].fd, ufds[1].events, + ufds[2].fd, ufds[2].events, ufds[3].fd, ufds[3].events, + nfds, timeout); + } else { + Debug4("poll({%d,0x%02hx,}, , %u, %d)", ufds[0].fd, ufds[0].events, nfds, timeout); + } result = poll(ufds, nfds, timeout); - Debug2("poll(, {,, 0x%02hx}) -> %d", ufds[0].revents, result); + if (nfds == 4) { + Debug5("poll(, {,,0x%02hx}{,,0x%02hx}{,,0x%02hx}{,,0x%02hx}) -> %d", + ufds[0].revents, ufds[1].revents, ufds[2].revents, ufds[3].revents, result); + } else { + Debug2("poll(, {,,0x%02hx}) -> %d", ufds[0].revents, result); + } return result; } #endif /* HAVE_POLL */ diff --git a/sysutils.c b/sysutils.c index f2266c7..d113e4a 100644 --- a/sysutils.c +++ b/sysutils.c @@ -1,5 +1,5 @@ /* source: sysutils.c */ -/* Copyright Gerhard Rieger 2001-2007 */ +/* Copyright Gerhard Rieger 2001-2008 */ /* Published under the GNU General Public License V.2, see file COPYING */ /* translate socket addresses into human readable form */ @@ -400,6 +400,17 @@ const char *hstrerror(int err) { return h_messages[err]; } #endif /* !HAVE_HSTRERROR */ + + +/* this function behaves like poll(). It tries to do so even when the poll() + system call is not available. */ +int xiopoll(struct pollfd fds[], nfds_t nfds, int timeout) { +#if HAVE_POLL + return Poll(fds, nfds, timeout); +#else /* HAVE_POLL */ + /*!!! wrap around Select() */ +#endif /* !HAVE_POLL */ +} #if WITH_TCP || WITH_UDP diff --git a/sysutils.h b/sysutils.h index c75e9a7..b9074b3 100644 --- a/sysutils.h +++ b/sysutils.h @@ -90,6 +90,8 @@ extern int getusergroups(const char *user, gid_t *list, size_t *ngroups); extern const char *hstrerror(int err); #endif +extern int xiopoll(struct pollfd fds[], nfds_t nfds, int timeout); + extern int parseport(const char *portname, int proto); extern int ifindexbyname(const char *ifname); diff --git a/xio-socket.c b/xio-socket.c index 36fdb59..bca7746 100644 --- a/xio-socket.c +++ b/xio-socket.c @@ -269,19 +269,21 @@ int _xioopen_connect(struct single *xfd, struct sockaddr *us, size_t uslen, if (errno == EINPROGRESS) { if (xfd->para.socket.connect_timeout.tv_sec != 0 || xfd->para.socket.connect_timeout.tv_usec != 0) { - struct timeval timeout; - fd_set readfds, writefds, exceptfds; + int timeout; + struct pollfd writefd; int result; + Info4("connect(%d, %s, "F_Zd"): %s", xfd->fd, sockaddr_info(them, themlen, infobuff, sizeof(infobuff)), themlen, strerror(errno)); - timeout = xfd->para.socket.connect_timeout; - FD_ZERO(&readfds); FD_ZERO(&writefds); FD_ZERO(&exceptfds); - FD_SET(xfd->fd, &readfds); FD_SET(xfd->fd, &writefds); - result = - Select(xfd->fd+1, &readfds, &writefds, &exceptfds, &timeout); + timeout = 1000*xfd->para.socket.connect_timeout.tv_sec + + xfd->para.socket.connect_timeout.tv_usec/1000; + writefd.fd = xfd->fd; + writefd.events = (POLLIN|POLLHUP|POLLERR); + result = Poll(&writefd, 1, timeout); if (result < 0) { - Msg2(level, "select(%d,,,,): %s", xfd->fd+1, strerror(errno)); + Msg3(level, "poll({%d,POLLIN|POLLHUP|POLLER},,%d): %s", + xfd->fd, timeout, strerror(errno)); return STAT_RETRYLATER; } if (result == 0) { @@ -290,7 +292,7 @@ int _xioopen_connect(struct single *xfd, struct sockaddr *us, size_t uslen, strerror(ETIMEDOUT)); return STAT_RETRYLATER; } - if (FD_ISSET(xfd->fd, &readfds)) { + if (writefd.revents & POLLOUT) { #if 0 unsigned char dummy[1]; Read(xfd->fd, &dummy, 1); /* get error message */ @@ -726,16 +728,16 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags, /* loop until select() returns valid */ do { - fd_set in, out, expt; + struct pollfd readfd; /*? int level = E_ERROR;*/ if (us != NULL) { Notice1("receiving on %s", sockaddr_info(us, uslen, lisname, sizeof(lisname))); } else { Notice1("receiving IP protocol %u", proto); } - FD_ZERO(&in); FD_ZERO(&out); FD_ZERO(&expt); - FD_SET(xfd->fd, &in); - if (Select(xfd->fd+1, &in, &out, &expt, NULL) > 0) { + readfd.fd = xfd->fd; + readfd.events = POLLIN; + if (Poll(&readfd, 1, -1) > 0) { break; } @@ -743,7 +745,7 @@ int _xioopen_dgram_recvfrom(struct single *xfd, int xioflags, continue; } - Msg2(level, "select(, {%d}): %s", xfd->fd, strerror(errno)); + Msg2(level, "poll({%d,,},,-1): %s", xfd->fd, strerror(errno)); Close(xfd->fd); return STAT_RETRYLATER; } while (true); diff --git a/xio-udp.c b/xio-udp.c index 3907f98..8cedcff 100644 --- a/xio-udp.c +++ b/xio-udp.c @@ -84,7 +84,7 @@ int xioopen_ipdgram_listen(int argc, const char *argv[], struct opt *opts, union sockaddr_union themunion; union sockaddr_union *them = &themunion; int socktype = SOCK_DGRAM; - fd_set in, out, expt; + struct pollfd readfd; bool dofork = false; pid_t pid; char *rangename; @@ -205,9 +205,9 @@ int xioopen_ipdgram_listen(int argc, const char *argv[], struct opt *opts, Notice1("listening on UDP %s", sockaddr_info(&us.soa, uslen, infobuff, sizeof(infobuff))); - FD_ZERO(&in); FD_ZERO(&out); FD_ZERO(&expt); - FD_SET(fd->stream.fd, &in); - while (Select(fd->stream.fd+1, &in, &out, &expt, NULL) < 0) { + readfd.fd = fd->stream.fd; + readfd.events = POLLIN|POLLERR; + while (Poll(&readfd, 1, -1) < 0) { if (errno != EINTR) break; }