diff options
Diffstat (limited to 'src/util/poll_fd.c')
-rw-r--r-- | src/util/poll_fd.c | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/src/util/poll_fd.c b/src/util/poll_fd.c new file mode 100644 index 0000000..80cd0f6 --- /dev/null +++ b/src/util/poll_fd.c @@ -0,0 +1,269 @@ +/*++ +/* NAME +/* poll_fd 3 +/* SUMMARY +/* wait until file descriptor becomes readable or writable +/* SYNOPSIS +/* #include <iostuff.h> +/* +/* int readable(fd) +/* int fd; +/* +/* int writable(fd) +/* int fd; +/* +/* int read_wait(fd, time_limit) +/* int fd; +/* int time_limit; +/* +/* int write_wait(fd, time_limit) +/* int fd; +/* int time_limit; +/* +/* int poll_fd(fd, request, time_limit, true_res, false_res) +/* int fd; +/* int request; +/* int time_limit; +/* int true_res; +/* int false_res; +/* DESCRIPTION +/* The read*() and write*() functions in this module are macros +/* that provide a convenient interface to poll_fd(). +/* +/* readable() asks the kernel if the specified file descriptor +/* is readable, i.e. a read operation would not block. +/* +/* writable() asks the kernel if the specified file descriptor +/* is writable, i.e. a write operation would not block. +/* +/* read_wait() waits until the specified file descriptor becomes +/* readable, or until the time limit is reached. +/* +/* write_wait() waits until the specified file descriptor +/* becomes writable, or until the time limit is reached. +/* +/* poll_fd() waits until the specified file descriptor becomes +/* readable or writable, or until the time limit is reached. +/* +/* Arguments: +/* .IP fd +/* File descriptor. With implementations based on select(), a +/* best effort is made to handle descriptors >=FD_SETSIZE. +/* .IP request +/* POLL_FD_READ (wait until readable) or POLL_FD_WRITE (wait +/* until writable). +/* .IP time_limit +/* A positive value specifies a time limit in seconds. A zero +/* value effects a poll (return immediately). A negative value +/* means wait until the requested POLL_FD_READ or POLL_FD_WRITE +/* condition becomes true. +/* .IP true_res +/* Result value when the requested POLL_FD_READ or POLL_FD_WRITE +/* condition is true. +/* .IP false_res +/* Result value when the requested POLL_FD_READ or POLL_FD_WRITE +/* condition is false. +/* DIAGNOSTICS +/* Panic: interface violation. All system call errors are fatal +/* unless specified otherwise. +/* +/* readable() and writable() return 1 when the requested +/* POLL_FD_READ or POLL_FD_WRITE condition is true, zero when +/* it is false. They never return an error indication. +/* +/* read_wait() and write_wait() return zero when the requested +/* POLL_FD_READ or POLL_FD_WRITE condition is true, -1 (with +/* errno set to ETIMEDOUT) when it is false. +/* +/* poll_fd() returns true_res when the requested POLL_FD_READ +/* or POLL_FD_WRITE condition is true, false_res when it is +/* false. When poll_fd() returns a false_res value < 0, it +/* also sets errno to ETIMEDOUT. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include <sys_defs.h> +#include <sys/time.h> +#include <signal.h> +#include <errno.h> +#include <unistd.h> +#include <string.h> + + /* + * Use poll() with fall-back to select(). MacOSX needs this for devices. + */ +#if defined(USE_SYSV_POLL_THEN_SELECT) +#define poll_fd_sysv poll_fd +#define USE_SYSV_POLL +#define USE_BSD_SELECT +int poll_fd_bsd(int, int, int, int, int); + + /* + * Use select() only. + */ +#elif defined(USE_BSD_SELECT) +#define poll_fd_bsd poll_fd +#undef USE_SYSV_POLL + + /* + * Use poll() only. + */ +#elif defined(USE_SYSV_POLL) +#define poll_fd_sysv poll_fd + + /* + * Sanity check. + */ +#else +#error "specify USE_SYSV_POLL, USE_BSD_SELECT or USE_SYSV_POLL_THEN_SELECT" +#endif + +#ifdef USE_SYSV_POLL +#include <poll.h> +#endif + +#ifdef USE_SYS_SELECT_H +#include <sys/select.h> +#endif + +/* Utility library. */ + +#include <msg.h> +#include <iostuff.h> + +#ifdef USE_BSD_SELECT + +/* poll_fd_bsd - block with time_limit until file descriptor is ready */ + +int poll_fd_bsd(int fd, int request, int time_limit, + int true_res, int false_res) +{ + fd_set req_fds; + fd_set *read_fds; + fd_set *write_fds; + fd_set except_fds; + struct timeval tv; + struct timeval *tp; + int temp_fd = -1; + + /* + * Sanity checks. + */ + if (FD_SETSIZE <= fd) { + if ((temp_fd = dup(fd)) < 0 || temp_fd >= FD_SETSIZE) + msg_fatal("descriptor %d does not fit FD_SETSIZE %d", fd, FD_SETSIZE); + fd = temp_fd; + } + + /* + * Use select() so we do not depend on alarm() and on signal() handlers. + * Restart select() when interrupted by some signal. Some select() + * implementations reduce the time to wait when interrupted, which is + * exactly what we want. + */ + FD_ZERO(&req_fds); + FD_SET(fd, &req_fds); + except_fds = req_fds; + if (request == POLL_FD_READ) { + read_fds = &req_fds; + write_fds = 0; + } else if (request == POLL_FD_WRITE) { + read_fds = 0; + write_fds = &req_fds; + } else { + msg_panic("poll_fd: bad request %d", request); + } + + if (time_limit >= 0) { + tv.tv_usec = 0; + tv.tv_sec = time_limit; + tp = &tv; + } else { + tp = 0; + } + + for (;;) { + switch (select(fd + 1, read_fds, write_fds, &except_fds, tp)) { + case -1: + if (errno != EINTR) + msg_fatal("select: %m"); + continue; + case 0: + if (temp_fd != -1) + (void) close(temp_fd); + if (false_res < 0) + errno = ETIMEDOUT; + return (false_res); + default: + if (temp_fd != -1) + (void) close(temp_fd); + return (true_res); + } + } +} + +#endif + +#ifdef USE_SYSV_POLL + +#ifdef USE_SYSV_POLL_THEN_SELECT +#define HANDLE_SYSV_POLL_ERROR(fd, req, time_limit, true_res, false_res) \ + return (poll_fd_bsd((fd), (req), (time_limit), (true_res), (false_res))) +#else +#define HANDLE_SYSV_POLL_ERROR(fd, req, time_limit, true_res, false_res) \ + msg_fatal("poll: %m") +#endif + +/* poll_fd_sysv - block with time_limit until file descriptor is ready */ + +int poll_fd_sysv(int fd, int request, int time_limit, + int true_res, int false_res) +{ + struct pollfd pollfd; + + /* + * System-V poll() is optimal for polling a few descriptors. + */ +#define WAIT_FOR_EVENT (-1) + + pollfd.fd = fd; + if (request == POLL_FD_READ) { + pollfd.events = POLLIN; + } else if (request == POLL_FD_WRITE) { + pollfd.events = POLLOUT; + } else { + msg_panic("poll_fd: bad request %d", request); + } + + for (;;) { + switch (poll(&pollfd, 1, time_limit < 0 ? + WAIT_FOR_EVENT : time_limit * 1000)) { + case -1: + if (errno != EINTR) + HANDLE_SYSV_POLL_ERROR(fd, request, time_limit, + true_res, false_res); + continue; + case 0: + if (false_res < 0) + errno = ETIMEDOUT; + return (false_res); + default: + if (pollfd.revents & POLLNVAL) + HANDLE_SYSV_POLL_ERROR(fd, request, time_limit, + true_res, false_res); + return (true_res); + } + } +} + +#endif |