diff --git a/src/private.h b/src/private.h index 6eb960d..c8100e9 100644 --- a/src/private.h +++ b/src/private.h @@ -46,6 +46,7 @@ typedef struct { int fd; /* For epoll() */ uint32_t errors; LIST_HEAD(,uev) watchers; + uint32_t workaround; /* For workarounds, e.g. redirected stdin */ } uev_ctx_t; /* Forward declare due to dependencys, don't try this at home kids. */ diff --git a/src/uev.c b/src/uev.c index 4d25981..1c71804 100644 --- a/src/uev.c +++ b/src/uev.c @@ -26,6 +26,7 @@ #include /* O_CLOEXEC */ #include /* memset() */ #include +#include /* for select() workaround */ #include /* struct signalfd_siginfo */ #include /* close(), read() */ @@ -56,6 +57,18 @@ static int is_valid_fd(int fd) return fcntl(fd, F_GETFL) != -1 || errno != EBADF; } +/* Used by file i/o workaround when epoll => EPERM */ +static int has_data(int fd) +{ + struct timeval timeout = { 0, 0 }; + fd_set fds; + + FD_ZERO(&fds); + FD_SET(fd, &fds); + + return select(1, &fds, NULL, NULL, &timeout) > 0; +} + /* Private to libuEv, do not use directly! */ int _uev_watcher_init(uev_ctx_t *ctx, uev_t *w, uev_type_t type, uev_cb_t *cb, void *arg, int fd, int events) { @@ -90,10 +103,23 @@ int _uev_watcher_start(uev_t *w) ev.events = w->events | EPOLLRDHUP; ev.data.ptr = w; - if (epoll_ctl(w->ctx->fd, EPOLL_CTL_ADD, w->fd, &ev) < 0) - return -1; + if (epoll_ctl(w->ctx->fd, EPOLL_CTL_ADD, w->fd, &ev) < 0) { + if (errno != EPERM) + return -1; - w->active = 1; + /* Handle special case: `application < file.txt` */ + if (w->type != UEV_IO_TYPE || w->events != UEV_READ) + return -1; + + /* Only allow this special handling for stdin */ + if (w->fd != STDIN_FILENO) + return -1; + + w->ctx->workaround = 1; + w->active = -1; + } else { + w->active = 1; + } /* Add to internal list for bookkeeping */ LIST_INSERT_HEAD(&w->ctx->watchers, w, link); @@ -254,9 +280,30 @@ int uev_run(uev_ctx_t *ctx, int flags) } while (ctx->running) { - int i, nfds; + int i, nfds, rerun = 0; struct epoll_event events[UEV_MAX_EVENTS]; + /* Handle special case: `application < file.txt` */ + if (ctx->workaround) { + LIST_FOREACH(w, &ctx->watchers, link) { + if (w->active != -1 || !w->cb) + continue; + + if (!has_data(w->fd)) { + w->active = 0; + LIST_REMOVE(w, link); + continue; + } + + rerun++; + w->cb(w, w->arg, UEV_READ); + } + } + + if (rerun) + continue; + ctx->workaround = 0; + while ((nfds = epoll_wait(ctx->fd, events, UEV_MAX_EVENTS, timeout)) < 0) { if (!ctx->running) break;