]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
eloop: Add eloop_openfdwaiter() and eloop_closefdwaiter()
authorRoy Marples <roy@marples.name>
Thu, 21 May 2026 22:48:45 +0000 (23:48 +0100)
committerRoy Marples <roy@marples.name>
Thu, 21 May 2026 23:31:46 +0000 (00:31 +0100)
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.

src/eloop.c
src/eloop.h

index cac15a1dc863f6b102dbbbc2fc01f93b9fcca784..9c62419a9a8d1be04488354fd106c4e5744bb1da 100644 (file)
@@ -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);
        }
 
index 95b9afc9c732de62b65ef07a694d9c2320334e15..acd183b002402c42e686cf6888d0ab923102103c 100644 (file)
@@ -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