/* * uloop - event loop implementation * * Copyright (C) 2010-2016 Felix Fietkau * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include /** * FIXME: uClibc < 0.9.30.3 does not define EPOLLRDHUP for Linux >= 2.6.17 */ #ifndef EPOLLRDHUP #define EPOLLRDHUP 0x2000 #endif static void uloop_signal_fd_cb(struct uloop_fd *fd, unsigned int events) { struct signalfd_siginfo fdsi; struct sigaction act; int ret; retry: ret = read(fd->fd, &fdsi, sizeof(fdsi)); if (ret < 0 && errno == EINTR) goto retry; if (ret != sizeof(fdsi)) return; switch (fdsi.ssi_signo) { case SIGQUIT: case SIGINT: case SIGTERM: sigaction(fdsi.ssi_signo, NULL, &act); if (act.sa_handler != SIG_IGN && act.sa_handler != SIG_DFL) { act.sa_handler(fdsi.ssi_signo); break; } /* fall through */ default: uloop_handle_signal(fdsi.ssi_signo); break; } } static bool uloop_setup_signalfd(bool add) { static struct uloop_fd sfd = { .cb = uloop_signal_fd_cb }; static sigset_t prev_mask; sigset_t mask; if (signal_fd < 0) return false; sigemptyset(&mask); if (!add) { uloop_fd_delete(&sfd); sigprocmask(SIG_BLOCK, &prev_mask, NULL); } else { sigaddset(&mask, SIGQUIT); sigaddset(&mask, SIGINT); sigaddset(&mask, SIGTERM); sigaddset(&mask, SIGCHLD); sigprocmask(SIG_BLOCK, &mask, &prev_mask); sfd.fd = signal_fd; uloop_fd_add(&sfd, ULOOP_READ | ULOOP_EDGE_TRIGGER); } if (signalfd(signal_fd, &mask, SFD_NONBLOCK | SFD_CLOEXEC) < 0) { sigprocmask(SIG_BLOCK, &prev_mask, NULL); return false; } return true; } int uloop_init(void) { sigset_t mask; if (poll_fd >= 0) return 0; poll_fd = epoll_create(32); if (poll_fd < 0) return -1; fcntl(poll_fd, F_SETFD, fcntl(poll_fd, F_GETFD) | FD_CLOEXEC); sigemptyset(&mask); signal_fd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC); return 0; } static int register_poll(struct uloop_fd *fd, unsigned int flags) { struct epoll_event ev; int op = fd->registered ? EPOLL_CTL_MOD : EPOLL_CTL_ADD; memset(&ev, 0, sizeof(struct epoll_event)); if (flags & ULOOP_READ) ev.events |= EPOLLIN | EPOLLRDHUP; if (flags & ULOOP_WRITE) ev.events |= EPOLLOUT; if (flags & ULOOP_EDGE_TRIGGER) ev.events |= EPOLLET; ev.data.fd = fd->fd; ev.data.ptr = fd; fd->flags = flags; return epoll_ctl(poll_fd, op, fd->fd, &ev); } static struct epoll_event events[ULOOP_MAX_EVENTS]; static int __uloop_fd_delete(struct uloop_fd *sock) { sock->flags = 0; return epoll_ctl(poll_fd, EPOLL_CTL_DEL, sock->fd, 0); } static int uloop_fetch_events(int timeout) { int n, nfds; nfds = epoll_wait(poll_fd, events, ARRAY_SIZE(events), timeout); for (n = 0; n < nfds; ++n) { struct uloop_fd_event *cur = &cur_fds[n]; struct uloop_fd *u = events[n].data.ptr; unsigned int ev = 0; cur->fd = u; if (!u) continue; if (events[n].events & (EPOLLERR|EPOLLHUP)) { u->error = true; if (!(u->flags & ULOOP_ERROR_CB)) uloop_fd_delete(u); } if(!(events[n].events & (EPOLLRDHUP|EPOLLIN|EPOLLOUT|EPOLLERR|EPOLLHUP))) { cur->fd = NULL; continue; } if(events[n].events & EPOLLRDHUP) u->eof = true; if(events[n].events & EPOLLIN) ev |= ULOOP_READ; if(events[n].events & EPOLLOUT) ev |= ULOOP_WRITE; cur->events = ev; } return nfds; }