From: Roy Marples Date: Thu, 21 May 2026 22:48:45 +0000 (+0100) Subject: eloop: Add eloop_openfdwaiter() and eloop_closefdwaiter() X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=6b8d4e11ffedd1d2e02c987a94c2c8569ac28be1;p=thirdparty%2Fdhcpcd.git eloop: Add eloop_openfdwaiter() and eloop_closefdwaiter() Use kqueue or epoll for eloop_waitfd(). This requires opening a new kqueue or epoll to handle this as this is a one shot event we don't want to touch the eloop events. This works with RLIMIT_NOFILE = 0. --- diff --git a/src/eloop.c b/src/eloop.c index cac15a1d..9c62419a 100644 --- a/src/eloop.c +++ b/src/eloop.c @@ -131,6 +131,7 @@ struct eloop { #if defined(USE_KQUEUE) || defined(USE_EPOLL) int fd; + int waitfd; #endif #if defined(USE_KQUEUE) struct kevent *fds; @@ -261,8 +262,7 @@ eloop_signal_kqueue(struct eloop *eloop, const int *signals, size_t nsignals) } static int -eloop_event_kqueue(struct eloop *eloop, struct eloop_event *e, - unsigned short events) +eloop_event_kqueue(int fd, struct eloop_event *e, unsigned short events) { #ifdef EVFILT_PROCDESC #define NKE 3 @@ -270,27 +270,25 @@ eloop_event_kqueue(struct eloop *eloop, struct eloop_event *e, #define NKE 2 #endif struct kevent ke[NKE], *kep = ke; - int fd = e->fd; + uintptr_t efd = (uintptr_t)e->fd; if (events & ELE_READ && !(e->events & ELE_READ)) - EV_SET(kep++, (uintptr_t)fd, EVFILT_READ, EV_ADD, 0, 0, e); + EV_SET(kep++, efd, EVFILT_READ, EV_ADD, 0, 0, e); else if (!(events & ELE_READ) && e->events & ELE_READ) - EV_SET(kep++, (uintptr_t)fd, EVFILT_READ, EV_DELETE, 0, 0, e); + EV_SET(kep++, efd, EVFILT_READ, EV_DELETE, 0, 0, e); if (events & ELE_WRITE && !(e->events & ELE_WRITE)) - EV_SET(kep++, (uintptr_t)fd, EVFILT_WRITE, EV_ADD, 0, 0, e); + EV_SET(kep++, efd, EVFILT_WRITE, EV_ADD, 0, 0, e); else if (!(events & ELE_WRITE) && e->events & ELE_WRITE) - EV_SET(kep++, (uintptr_t)fd, EVFILT_WRITE, EV_DELETE, 0, 0, e); + EV_SET(kep++, efd, EVFILT_WRITE, EV_DELETE, 0, 0, e); #ifdef EVFILT_PROCDESC if (events & ELE_HANGUP && !(e->events & ELE_HANGUP)) - EV_SET(kep++, (uintptr_t)fd, EVFILT_PROCDESC, EV_ADD, NOTE_EXIT, - 0, e); + EV_SET(kep++, efd, EVFILT_PROCDESC, EV_ADD, NOTE_EXIT, 0, e); else if (!(events & ELE_HANGUP) && e->events & ELE_HANGUP) - EV_SET(kep++, (uintptr_t)fd, EVFILT_PROCDESC, EV_DELETE, - NOTE_EXIT, 0, e); + EV_SET(kep++, efd, EVFILT_PROCDESC, EV_DELETE, NOTE_EXIT, 0, e); #endif if (kep == ke) return 0; - if (kevent(eloop->fd, ke, (KEVENT_N)(kep - ke), NULL, 0, NULL) == -1) + if (kevent(fd, ke, (KEVENT_N)(kep - ke), NULL, 0, NULL) == -1) return -1; return 1; } @@ -298,8 +296,7 @@ eloop_event_kqueue(struct eloop *eloop, struct eloop_event *e, #elif defined(USE_EPOLL) static int -eloop_event_epoll(struct eloop *eloop, struct eloop_event *e, - unsigned short events) +eloop_event_epoll(int fd, struct eloop_event *e, unsigned short events) { struct epoll_event epe; int op; @@ -310,10 +307,13 @@ eloop_event_epoll(struct eloop *eloop, struct eloop_event *e, epe.events |= EPOLLIN; if (events & ELE_WRITE) epe.events |= EPOLLOUT; - op = e->events == 0 ? EPOLL_CTL_ADD : EPOLL_CTL_MOD; + if (events & ELE_HANGUP) + epe.events |= EPOLLHUP; if (epe.events == 0) - return 0; - if (epoll_ctl(eloop->fd, op, e->fd, &epe) == -1) + op = EPOLL_CTL_DEL; + else + op = e->events == 0 ? EPOLL_CTL_ADD : EPOLL_CTL_MOD; + if (epoll_ctl(fd, op, e->fd, &epe) == -1) return -1; return 1; } @@ -359,9 +359,9 @@ eloop_event_add(struct eloop *eloop, int fd, unsigned short events, e->cb_arg = cb_arg; #if defined(USE_KQUEUE) - kadded = eloop_event_kqueue(eloop, e, events); + kadded = eloop_event_kqueue(eloop->fd, e, events); #elif defined(USE_EPOLL) - kadded = eloop_event_epoll(eloop, e, events); + kadded = eloop_event_epoll(eloop->fd, e, events); #elif defined(USE_PPOLL) e->pollfd = NULL; kadded = 1; @@ -621,7 +621,7 @@ eloop_exit(struct eloop *eloop, int code) #if defined(USE_KQUEUE) || defined(USE_EPOLL) static int -eloop_open(struct eloop *eloop) +eloop_open(void) { int fd; @@ -649,7 +649,6 @@ eloop_open(struct eloop *eloop) fd = epoll_create1(EPOLL_CLOEXEC); #endif - eloop->fd = fd; return fd; } #endif @@ -710,8 +709,23 @@ eloop_forked(struct eloop *eloop, unsigned short flags) close(eloop->fd); #endif eloop->fd = -1; - if (flags && eloop_open(eloop) == -1) - return -1; + if (flags) { + if ((eloop->fd = eloop_open()) == -1) + return -1; + + if (eloop->waitfd != -1) { +#ifdef USE_EPOLL + close(eloop->waitfd); +#endif + if ((eloop->waitfd = eloop_open()) == -1) + return -1; + } + } else { +#ifdef USE_EPOLL + close(eloop->waitfd); +#endif + eloop->waitfd = -1; + } eloop_clear(eloop, flags); if (!flags) @@ -728,9 +742,9 @@ eloop_forked(struct eloop *eloop, unsigned short flags) events = e->events; e->events = 0; #if defined(USE_KQUEUE) - err = eloop_event_kqueue(eloop, e, events); + err = eloop_event_kqueue(eloop->fd, e, events); #elif defined(USE_EPOLL) - err = eloop_event_epoll(eloop, e, events); + err = eloop_event_epoll(eloop->fd, e, events); #endif if (err == -1) return -1; @@ -837,7 +851,9 @@ eloop_new(void) eloop->exitcode = EXIT_FAILURE; #if defined(USE_KQUEUE) || defined(USE_EPOLL) - if (eloop_open(eloop) == -1 || eloop_grow_events(eloop) == -1) { + eloop->waitfd = -1; + if ((eloop->fd = eloop_open()) == -1 || + eloop_grow_events(eloop) == -1) { eloop_free(eloop); return NULL; } @@ -854,6 +870,8 @@ eloop_free(struct eloop *eloop) eloop_clear(eloop, 0); #if defined(USE_KQUEUE) || defined(USE_EPOLL) + if (eloop->waitfd != -1) + close(eloop->waitfd); if (eloop->fd != -1) close(eloop->fd); #endif @@ -861,6 +879,48 @@ eloop_free(struct eloop *eloop) free(eloop); } +#if defined(USE_KQUEUE) +static unsigned short +eloop_kqueueevents(struct kevent *ke) +{ + unsigned short events; + + if (ke->filter == EVFILT_READ) + events = ELE_READ; + else if (ke->filter == EVFILT_WRITE) + events = ELE_WRITE; +#ifdef EVFILT_PROCDESC + else if (ke->filter == EVFILT_PROCDESC && ke->fflags & NOTE_EXIT) + /* exit status is in ke->data + * should we do anything with it? */ + events = ELE_HANGUP; +#endif + else + events = 0; + if (ke->flags & EV_EOF) + events |= ELE_HANGUP; + if (ke->flags & EV_ERROR) + events |= ELE_ERROR; + return events; +} +#elif defined(USE_EPOLL) +static unsigned short +eloop_epollevents(struct epoll_event *epe) +{ + unsigned short events = 0; + + if (epe->events & EPOLLIN) + events |= ELE_READ; + if (epe->events & EPOLLOUT) + events |= ELE_WRITE; + if (epe->events & EPOLLHUP) + events |= ELE_HANGUP; + if (epe->events & EPOLLERR) + events |= ELE_ERROR; + + return events; +} +#elif defined(USE_POLL) static unsigned short eloop_pollevents(struct pollfd *pfd) { @@ -878,18 +938,71 @@ eloop_pollevents(struct pollfd *pfd) events |= ELE_NVAL; return events; } +#endif int -eloop_waitfd(int fd) +eloop_openfdwaiter(struct eloop *eloop) { - struct pollfd pfd = { .fd = fd, .events = POLLIN }; - int err; +#if defined(USE_KQUEUE) || defined(USE_EPOLL) + int fd; + + fd = eloop_open(); + if (fd == -1) + return -1; + eloop->waitfd = fd; + return fd; +#else + /* Not an error, but no fd */ + errno = 0; + return -1; +#endif +} + +int +eloop_waitfd(struct eloop *eloop, int fd) +{ + int n; +#if defined(USE_KQUEUE) + struct kevent ke; + + EV_SET(&ke, (uintptr_t)fd, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, + NULL); + n = kevent(eloop->waitfd, &ke, 1, &ke, 1, NULL); + if (n == -1 || n == 0) + return n; + return (int)eloop_kqueueevents(&ke); +#elif defined(USE_EPOLL) + struct eloop_event e = { .fd = fd }; + struct epoll_event epe; - err = poll(&pfd, 1, -1); - if (err == -1 || err == 0) - return err; + if (eloop_event_epoll(eloop->waitfd, &e, ELE_READ) == -1) + return -1; + n = epoll_pwait(eloop->waitfd, &epe, 1, -1, NULL); + if (eloop_event_epoll(eloop->waitfd, &e, 0) == -1) + return -1; + if (n == -1 || n == 0) + return n; + return (int)eloop_epollevents(&epe); +#else + struct pollfd pfd = { .fd = fd, .events = POLLIN }; + n = poll(&pfd, 1, -1); + if (n == -1 || n == 0) + return n; return (int)eloop_pollevents(&pfd); +#endif +} + +int +eloop_closefdwaiter(struct eloop *eloop) +{ +#if defined(USE_KQUEUE) || defined(USE_EPOLL) + int err = close(eloop->waitfd); + eloop->waitfd = -1; + return err; +#else + return 0; +#endif } #if defined(USE_KQUEUE) @@ -915,23 +1028,7 @@ eloop_run_kqueue(struct eloop *eloop, const struct timespec *ts) eloop->signal_cb_ctx); continue; } - if (ke->filter == EVFILT_READ) - events = ELE_READ; - else if (ke->filter == EVFILT_WRITE) - events = ELE_WRITE; -#ifdef EVFILT_PROCDESC - else if (ke->filter == EVFILT_PROCDESC && - ke->fflags & NOTE_EXIT) - /* exit status is in ke->data - * should we do anything with it? */ - events = ELE_HANGUP; -#endif - else - continue; /* assert? */ - if (ke->flags & EV_EOF) - events |= ELE_HANGUP; - if (ke->flags & EV_ERROR) - events |= ELE_ERROR; + events = eloop_kqueueevents(ke); e = (struct eloop_event *)ke->udata; e->cb(e->cb_arg, events); } @@ -983,15 +1080,7 @@ eloop_run_epoll(struct eloop *eloop, const struct timespec *ts) e = (struct eloop_event *)epe->data.ptr; if (e->fd == -1) continue; - events = 0; - if (epe->events & EPOLLIN) - events |= ELE_READ; - if (epe->events & EPOLLOUT) - events |= ELE_WRITE; - if (epe->events & EPOLLHUP) - events |= ELE_HANGUP; - if (epe->events & EPOLLERR) - events |= ELE_ERROR; + events = eloop_epollevents(epe); e->cb(e->cb_arg, events); } diff --git a/src/eloop.h b/src/eloop.h index 95b9afc9..acd183b0 100644 --- a/src/eloop.h +++ b/src/eloop.h @@ -97,7 +97,10 @@ struct eloop *eloop_new(void); void eloop_free(struct eloop *); void eloop_exit(struct eloop *, int); int eloop_forked(struct eloop *, unsigned short); -int eloop_waitfd(int); int eloop_start(struct eloop *); +int eloop_openfdwaiter(struct eloop *); +int eloop_waitfd(struct eloop *, int); +int eloop_closefdwaiter(struct eloop *); + #endif