From 488a238ad77980452a2760ccbabad6eeff06db9c Mon Sep 17 00:00:00 2001 From: Stephen Heumann Date: Mon, 3 Nov 2014 12:26:43 -0600 Subject: [PATCH] Add an implementation of poll() written in terms of select(), to work around the lack of poll() on GNO. This is a BSD-licensed poll() implementation by Brian M. Clapper. Its performance characteristics aren't as good as a "native" poll() would be, but this shouldn't be a problem in practice. --- Makefile | 3 +- include/libbb.h | 10 +- include/poll.h | 105 +++++++++++++++ libbb/poll.c | 306 +++++++++++++++++++++++++++++++++++++++++++ shell/shell.common.c | 7 +- 5 files changed, 415 insertions(+), 16 deletions(-) create mode 100644 include/poll.h create mode 100644 libbb/poll.c diff --git a/Makefile b/Makefile index ffffe8992..2c67d4a06 100644 --- a/Makefile +++ b/Makefile @@ -51,7 +51,8 @@ SRCS = \ libbb/appletlib.c \ libbb/bb.basename.c \ libbb/mempcpy.c \ - libbb/vfork.and.run.c + libbb/vfork.and.run.c \ + libbb/poll.c OBJS = $(SRCS:.c=.o) ROOTS = $(SRCS:.c=.root) diff --git a/include/libbb.h b/include/libbb.h index 0a383fcfc..7ec6ef28f 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -34,11 +34,7 @@ /* See the dirname/basename man page for details */ #undef basename #define basename dont_use_basename -#ifndef __GNO__ -# include -#else -//TODO Deal with lack of poll in GNO -#endif +#include "poll.h" #include #include #include @@ -755,11 +751,7 @@ void qsort_string_vector(char **sv, unsigned count) FAST_FUNC; * On other errors complains [perror("poll")] and returns. * Warning! May take (much) longer than timeout_ms to return! * If this is a problem, use bare poll and open-code EINTR/ENOMEM handling */ -#ifndef __GNO__ int safe_poll(struct pollfd *ufds, nfds_t nfds, int timeout_ms) FAST_FUNC; -#else -//TODO Deal with lack of poll in GNO -#endif char *safe_gethostname(void) FAST_FUNC; diff --git a/include/poll.h b/include/poll.h new file mode 100644 index 000000000..1d294e850 --- /dev/null +++ b/include/poll.h @@ -0,0 +1,105 @@ +/*---------------------------------------------------------------------------*\ + $Id$ + + NAME + + poll - select(2)-based poll() emulation function for BSD systems. + + SYNOPSIS + #include "poll.h" + + struct pollfd + { + int fd; + short events; + short revents; + } + + int poll (struct pollfd *pArray, unsigned long n_fds, int timeout) + + DESCRIPTION + + This file, and the accompanying "poll.c", implement the System V + poll(2) system call for BSD systems (which typically do not provide + poll()). Poll() provides a method for multiplexing input and output + on multiple open file descriptors; in traditional BSD systems, that + capability is provided by select(). While the semantics of select() + differ from those of poll(), poll() can be readily emulated in terms + of select() -- which is how this function is implemented. + + REFERENCES + Stevens, W. Richard. Unix Network Programming. Prentice-Hall, 1990. + + NOTES + 1. This software requires an ANSI C compiler. + + LICENSE + + This software is released under the following BSD license, adapted from + http://opensource.org/licenses/bsd-license.php + + Copyright (c) 1995-2011, Brian M. Clapper + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the clapper.org nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\*---------------------------------------------------------------------------*/ + +#ifndef _POLL_EMUL_H_ +#define _POLL_EMUL_H_ + +#define POLLIN 0x01 +#define POLLPRI 0x02 +#define POLLOUT 0x04 +#define POLLERR 0x08 +#define POLLHUP 0x10 +#define POLLNVAL 0x20 + +struct pollfd +{ + int fd; + short events; + short revents; +}; + +typedef unsigned long nfds_t; + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if (__STDC__ > 0) || defined(__cplusplus) +extern int poll (struct pollfd *pArray, nfds_t n_fds, int timeout); +#else +extern int poll(); +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _POLL_EMUL_H_ */ diff --git a/libbb/poll.c b/libbb/poll.c new file mode 100644 index 000000000..6ec544663 --- /dev/null +++ b/libbb/poll.c @@ -0,0 +1,306 @@ +/*---------------------------------------------------------------------------*\ + $Id$ + + NAME + + poll - select(2)-based poll() emulation function for BSD systems. + + SYNOPSIS + #include "poll.h" + + struct pollfd + { + int fd; + short events; + short revents; + } + + int poll (struct pollfd *pArray, unsigned long n_fds, int timeout) + + DESCRIPTION + + This file, and the accompanying "poll.h", implement the System V + poll(2) system call for BSD systems (which typically do not provide + poll()). Poll() provides a method for multiplexing input and output + on multiple open file descriptors; in traditional BSD systems, that + capability is provided by select(). While the semantics of select() + differ from those of poll(), poll() can be readily emulated in terms + of select() -- which is how this function is implemented. + + REFERENCES + Stevens, W. Richard. Unix Network Programming. Prentice-Hall, 1990. + + NOTES + 1. This software requires an ANSI C compiler. + + LICENSE + + This software is released under the following BSD license, adapted from + http://opensource.org/licenses/bsd-license.php + + Copyright (c) 1995-2011, Brian M. Clapper + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of the clapper.org nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +\*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*\ + Includes +\*---------------------------------------------------------------------------*/ + +#include /* standard Unix definitions */ +#include /* system types */ +#include /* time definitions */ +#include /* assertion macros */ +#include /* string functions */ +#include "poll.h" /* this package */ + +/*---------------------------------------------------------------------------*\ + Macros +\*---------------------------------------------------------------------------*/ + +#ifndef MAX +#define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif + +/*---------------------------------------------------------------------------*\ + Private Functions +\*---------------------------------------------------------------------------*/ + +static int map_poll_spec +#if __STDC__ > 0 + (struct pollfd *pArray, + nfds_t n_fds, + fd_set *pReadSet, + fd_set *pWriteSet, + fd_set *pExceptSet) +#else + (pArray, n_fds, pReadSet, pWriteSet, pExceptSet) + struct pollfd *pArray; + nfds_t n_fds, + fd_set *pReadSet; + fd_set *pWriteSet; + fd_set *pExceptSet; +#endif +{ + register nfds_t i; /* loop control */ + register struct pollfd *pCur; /* current array element */ + register int max_fd = -1; /* return value */ + + /* + Map the poll() structures into the file descriptor sets required + by select(). + */ + for (i = 0, pCur = pArray; i < n_fds; i++, pCur++) + { + /* Skip any bad FDs in the array. */ + + if (pCur->fd < 0) + continue; + + if (pCur->events & POLLIN) + { + /* "Input Ready" notification desired. */ + FD_SET (pCur->fd, pReadSet); + } + + if (pCur->events & POLLOUT) + { + /* "Output Possible" notification desired. */ + FD_SET (pCur->fd, pWriteSet); + } + + if (pCur->events & POLLPRI) + { + /* + "Exception Occurred" notification desired. (Exceptions + include out of band data. + */ + FD_SET (pCur->fd, pExceptSet); + } + + max_fd = MAX (max_fd, pCur->fd); + } + + return max_fd; +} + +static struct timeval *map_timeout +#if __STDC__ > 0 + (int poll_timeout, struct timeval *pSelTimeout) +#else + (poll_timeout, pSelTimeout) + int poll_timeout; + struct timeval *pSelTimeout; +#endif +{ + struct timeval *pResult; + + /* + Map the poll() timeout value into a select() timeout. The possible + values of the poll() timeout value, and their meanings, are: + + VALUE MEANING + + -1 wait indefinitely (until signal occurs) + 0 return immediately, don't block + >0 wait specified number of milliseconds + + select() uses a "struct timeval", which specifies the timeout in + seconds and microseconds, so the milliseconds value has to be mapped + accordingly. + */ + + assert (pSelTimeout != (struct timeval *) NULL); + + switch (poll_timeout) + { + case -1: + /* + A NULL timeout structure tells select() to wait indefinitely. + */ + pResult = (struct timeval *) NULL; + break; + + case 0: + /* + "Return immediately" (test) is specified by all zeros in + a timeval structure. + */ + pSelTimeout->tv_sec = 0; + pSelTimeout->tv_usec = 0; + pResult = pSelTimeout; + break; + + default: + /* Wait the specified number of milliseconds. */ + pSelTimeout->tv_sec = poll_timeout / 1000; /* get seconds */ + poll_timeout %= 1000; /* remove seconds */ + pSelTimeout->tv_usec = poll_timeout * 1000; /* get microseconds */ + pResult = pSelTimeout; + break; + } + + + return pResult; +} + +static void map_select_results +#if __STDC__ > 0 + (struct pollfd *pArray, + unsigned long n_fds, + fd_set *pReadSet, + fd_set *pWriteSet, + fd_set *pExceptSet) +#else + (pArray, n_fds, pReadSet, pWriteSet, pExceptSet) + struct pollfd *pArray; + unsigned long n_fds; + fd_set *pReadSet; + fd_set *pWriteSet; + fd_set *pExceptSet; +#endif +{ + register unsigned long i; /* loop control */ + register struct pollfd *pCur; /* current array element */ + + for (i = 0, pCur = pArray; i < n_fds; i++, pCur++) + { + /* Skip any bad FDs in the array. */ + + if (pCur->fd < 0) + continue; + + /* Exception events take priority over input events. */ + + pCur->revents = 0; + if (FD_ISSET (pCur->fd, pExceptSet)) + pCur->revents |= POLLPRI; + + else if (FD_ISSET (pCur->fd, pReadSet)) + pCur->revents |= POLLIN; + + if (FD_ISSET (pCur->fd, pWriteSet)) + pCur->revents |= POLLOUT; + } + + return; +} + +/*---------------------------------------------------------------------------*\ + Public Functions +\*---------------------------------------------------------------------------*/ + +int poll + +#if __STDC__ > 0 + (struct pollfd *pArray, unsigned long n_fds, int timeout) +#else + (pArray, n_fds, timeout) + struct pollfd *pArray; + unsigned long n_fds; + int timeout; +#endif + +{ + fd_set read_descs; /* input file descs */ + fd_set write_descs; /* output file descs */ + fd_set except_descs; /* exception descs */ + struct timeval stime; /* select() timeout value */ + int ready_descriptors; /* function result */ + int max_fd; /* maximum fd value */ + struct timeval *pTimeout; /* actually passed */ + + FD_ZERO (&read_descs); + FD_ZERO (&write_descs); + FD_ZERO (&except_descs); + + assert (pArray != (struct pollfd *) NULL); + + /* Map the poll() file descriptor list in the select() data structures. */ + + max_fd = map_poll_spec (pArray, n_fds, + &read_descs, &write_descs, &except_descs); + + /* Map the poll() timeout value in the select() timeout structure. */ + + pTimeout = map_timeout (timeout, &stime); + + /* Make the select() call. */ + + ready_descriptors = select (max_fd + 1, &read_descs, &write_descs, + &except_descs, pTimeout); + + if (ready_descriptors >= 0) + { + map_select_results (pArray, n_fds, + &read_descs, &write_descs, &except_descs); + } + + return ready_descriptors; +} diff --git a/shell/shell.common.c b/shell/shell.common.c index 30abf9adb..8a3f9a210 100644 --- a/shell/shell.common.c +++ b/shell/shell.common.c @@ -188,19 +188,14 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), * regardless of SA_RESTART-ness of that signal! */ errno = 0; -#ifndef __GNO__ -// GNO doesn't have poll, so disable this for now. This disables the -t -// (timeout) flag and may interfere with signal handling, as mentioned above. -// TODO Do something more intelligent here. pfd[0].fd = fd; pfd[0].events = POLLIN; - if (poll(pfd, 1, timeout * 1000) != 1) { + if (poll(pfd, 1, timeout == -1 ? -1 : timeout * 1000) != 1) { /* timed out, or EINTR */ err = errno; retval = (const char *)(uintptr_t)1; goto ret; } -#endif if (read(fd, &buffer[bufpos], 1) != 1) { err = errno; retval = (const char *)(uintptr_t)1;