summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRoy Marples <roy@marples.name>2016-02-26 21:26:00 +0000
committerRoy Marples <roy@marples.name>2016-02-26 21:26:00 +0000
commit8b4023bab4673e76c5eea27cde0a85b5f3b82380 (patch)
tree7ccf48749e34a19fe9d3592a20b0393d03bf7c99
parente8d894cffcebaccdc64f5160de89e1c3c832f966 (diff)
downloadparpd-8b4023bab4673e76c5eea27cde0a85b5f3b82380.tar.xz
Sync eloop with master from dhcpcd.
-rw-r--r--eloop.c206
1 files changed, 125 insertions, 81 deletions
diff --git a/eloop.c b/eloop.c
index d260d0e..3c57f46 100644
--- a/eloop.c
+++ b/eloop.c
@@ -1,7 +1,7 @@
/*
- * dhcpcd - DHCP client daemon
+ * eloop - portable event based main loop.
* Copyright (c) 2006-2016 Roy Marples <roy@marples.name>
- * All rights reserved
+ * All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -34,11 +34,12 @@
#include <errno.h>
#include <limits.h>
#include <signal.h>
+#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-/* config.h should define HAVE_KQUEUE, HAVE_EPOLL, etc */
+/* config.h should define HAVE_KQUEUE, HAVE_EPOLL, etc. */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
@@ -48,11 +49,11 @@
#if !defined(HAVE_KQUEUE) && !defined(HAVE_EPOLL) && !defined(HAVE_PSELECT) && \
!defined(HAVE_POLLTS) && !defined(HAVE_PPOLL)
#if defined(BSD)
-/* Assume BSD has a working sys/queue.h and kqueue(2) interface */
+/* Assume BSD has a working sys/queue.h and kqueue(2) interface. */
#define HAVE_SYS_QUEUE_H
#define HAVE_KQUEUE
#elif defined(__linux__)
-/* Assume Linux has a working epoll(3) interface */
+/* Assume Linux has a working epoll(3) interface. */
#define HAVE_EPOLL
#else
/* pselect(2) is a POSIX standard. */
@@ -99,8 +100,8 @@
#include <sys/event.h>
#include <fcntl.h>
#ifdef __NetBSD__
-/* udata is void * except on NetBSD
- * lengths are int except on NetBSD */
+/* udata is void * except on NetBSD.
+ * lengths are int except on NetBSD. */
#define UPTR(x) ((intptr_t)(x))
#define LENC(x) (x)
#else
@@ -142,9 +143,6 @@ struct eloop_event {
void *read_cb_arg;
void (*write_cb)(void *);
void *write_cb_arg;
-#ifdef HAVE_POLL
- struct pollfd *pollfd;
-#endif
};
struct eloop_timeout {
@@ -159,6 +157,8 @@ struct eloop {
size_t events_len;
TAILQ_HEAD (event_head, eloop_event) events;
struct event_head free_events;
+ int events_maxfd;
+ struct eloop_event **event_fds;
TAILQ_HEAD (timeout_head, eloop_timeout) timeouts;
struct timeout_head free_timeouts;
@@ -181,6 +181,22 @@ struct eloop {
int exitcode;
};
+/* Handy routing to check for potential overflow.
+ * reallocarray(3) and reallocarr(3) are not portable and this
+ * implementation is smaller than using either in libc in
+ * the final binary size. */
+#define SQRT_SIZE_MAX (((size_t)1) << (sizeof(size_t) * CHAR_BIT / 2))
+static void *
+eloop_realloca(void *ptr, size_t n, size_t size)
+{
+
+ if ((n | size) >= SQRT_SIZE_MAX && n > SIZE_MAX / size) {
+ errno = EOVERFLOW;
+ return NULL;
+ }
+ return realloc(ptr, n * size);
+}
+
#ifdef HAVE_POLL
static void
eloop_event_setup_fds(struct eloop *eloop)
@@ -197,7 +213,6 @@ eloop_event_setup_fds(struct eloop *eloop)
if (e->write_cb)
eloop->fds[i].events |= POLLOUT;
eloop->fds[i].revents = 0;
- e->pollfd = &eloop->fds[i];
i++;
}
}
@@ -208,11 +223,12 @@ static int
eloop_pollts(struct pollfd * fds, nfds_t nfds,
const struct timespec *ts, const sigset_t *sigmask)
{
- fd_set read_fds;
+ fd_set read_fds, write_fds;
nfds_t n;
int maxfd, r;
FD_ZERO(&read_fds);
+ FD_ZERO(&write_fds);
maxfd = 0;
for (n = 0; n < nfds; n++) {
if (fds[n].events & POLLIN) {
@@ -220,13 +236,20 @@ eloop_pollts(struct pollfd * fds, nfds_t nfds,
if (fds[n].fd > maxfd)
maxfd = fds[n].fd;
}
+ if (fds[n].events & POLLOUT) {
+ FD_SET(fds[n].fd, &write_fds);
+ if (fds[n].fd > maxfd)
+ maxfd = fds[n].fd;
+ }
}
- r = pselect(maxfd + 1, &read_fds, NULL, NULL, ts, sigmask);
+ r = pselect(maxfd + 1, &read_fds, &write_fds, NULL, ts, sigmask);
if (r > 0) {
for (n = 0; n < nfds; n++) {
fds[n].revents =
FD_ISSET(fds[n].fd, &read_fds) ? POLLIN : 0;
+ if (FD_ISSET(fds[n].fd, &write_fds))
+ fds[n].revents |= POLLOUT;
}
}
@@ -266,9 +289,9 @@ eloop_event_add(struct eloop *eloop, int fd,
epe.events |= EPOLLOUT;
#endif
- /* We should only have one callback monitoring the fd */
- TAILQ_FOREACH(e, &eloop->events, next) {
- if (e->fd == fd) {
+ /* We should only have one callback monitoring the fd. */
+ if (fd <= eloop->events_maxfd) {
+ if ((e = eloop->event_fds[fd]) != NULL) {
int error;
#if defined(HAVE_KQUEUE)
@@ -300,9 +323,26 @@ eloop_event_add(struct eloop *eloop, int fd,
eloop_event_setup_fds(eloop);
return error;
}
+ } else {
+ struct eloop_event **new_fds;
+ int maxfd, i;
+
+ /* Reserve ourself and 4 more. */
+ maxfd = fd + 4;
+ new_fds = eloop_realloca(eloop->event_fds,
+ ((size_t)maxfd + 1), sizeof(*eloop->event_fds));
+ if (new_fds == NULL)
+ return -1;
+
+ /* set new entries NULL as the fd's may not be contiguous. */
+ for (i = maxfd; i > eloop->events_maxfd; i--)
+ new_fds[i] = NULL;
+
+ eloop->event_fds = new_fds;
+ eloop->events_maxfd = maxfd;
}
- /* Allocate a new event if no free ones already allocated */
+ /* Allocate a new event if no free ones already allocated. */
if ((e = TAILQ_FIRST(&eloop->free_events))) {
TAILQ_REMOVE(&eloop->free_events, e, next);
} else {
@@ -311,12 +351,12 @@ eloop_event_add(struct eloop *eloop, int fd,
goto err;
}
- /* Ensure we can actually listen to it */
+ /* Ensure we can actually listen to it. */
eloop->events_len++;
#ifdef HAVE_POLL
if (eloop->events_len > eloop->fds_len) {
- nfds = realloc(eloop->fds,
- sizeof(*eloop->fds) * (eloop->fds_len + 5));
+ nfds = eloop_realloca(eloop->fds,
+ (eloop->fds_len + 5), sizeof(*eloop->fds));
if (nfds == NULL)
goto err;
eloop->fds_len += 5;
@@ -324,7 +364,7 @@ eloop_event_add(struct eloop *eloop, int fd,
}
#endif
- /* Now populate the structure and add it to the list */
+ /* Now populate the structure and add it to the list. */
e->fd = fd;
e->read_cb = read_cb;
e->read_cb_arg = read_cb_arg;
@@ -346,13 +386,8 @@ eloop_event_add(struct eloop *eloop, int fd,
goto err;
#endif
- /* The order of events should not matter.
- * However, some PPP servers love to close the link right after
- * sending their final message. So to ensure dhcpcd processes this
- * message (which is likely to be that the DHCP addresses are wrong)
- * we insert new events at the queue head as the link fd will be
- * the first event added. */
TAILQ_INSERT_HEAD(&eloop->events, e, next);
+ eloop->event_fds[e->fd] = e;
eloop_event_setup_fds(eloop);
return 0;
@@ -376,52 +411,53 @@ eloop_event_delete_write(struct eloop *eloop, int fd, int write_only)
assert(eloop != NULL);
- TAILQ_FOREACH(e, &eloop->events, next) {
- if (e->fd == fd) {
- if (write_only && e->read_cb != NULL) {
- if (e->write_cb != NULL) {
- e->write_cb = NULL;
- e->write_cb_arg = NULL;
+ if (fd > eloop->events_maxfd ||
+ (e = eloop->event_fds[fd]) == NULL)
+ return;
+
+ if (write_only) {
+ if (e->write_cb == NULL)
+ return;
+ if (e->read_cb == NULL)
+ goto remove;
+ e->write_cb = NULL;
+ e->write_cb_arg = NULL;
#if defined(HAVE_KQUEUE)
- EV_SET(&ke[0], (uintptr_t)fd,
- EVFILT_WRITE, EV_DELETE,
- 0, 0, UPTR(NULL));
- kevent(eloop->poll_fd, ke, 1, NULL, 0,
- NULL);
+ EV_SET(&ke[0], (uintptr_t)e->fd,
+ EVFILT_WRITE, EV_DELETE, 0, 0, UPTR(NULL));
+ kevent(eloop->poll_fd, ke, 1, NULL, 0, NULL);
#elif defined(HAVE_EPOLL)
- memset(&epe, 0, sizeof(epe));
- epe.data.fd = e->fd;
- epe.data.ptr = e;
- epe.events = EPOLLIN;
- epoll_ctl(eloop->poll_fd,
- EPOLL_CTL_MOD, fd, &epe);
+ memset(&epe, 0, sizeof(epe));
+ epe.data.fd = e->fd;
+ epe.data.ptr = e;
+ epe.events = EPOLLIN;
+ epoll_ctl(eloop->poll_fd, EPOLL_CTL_MOD, fd, &epe);
#endif
- }
- } else {
- TAILQ_REMOVE(&eloop->events, e, next);
+ eloop_event_setup_fds(eloop);
+ return;
+ }
+
+remove:
+ TAILQ_REMOVE(&eloop->events, e, next);
+ eloop->event_fds[e->fd] = NULL;
+ TAILQ_INSERT_TAIL(&eloop->free_events, e, next);
+ eloop->events_len--;
+
#if defined(HAVE_KQUEUE)
- EV_SET(&ke[0], (uintptr_t)fd, EVFILT_READ,
- EV_DELETE, 0, 0, UPTR(NULL));
- if (e->write_cb)
- EV_SET(&ke[1], (uintptr_t)fd,
- EVFILT_WRITE, EV_DELETE,
- 0, 0, UPTR(NULL));
- kevent(eloop->poll_fd, ke, e->write_cb ? 2 : 1,
- NULL, 0, NULL);
+ EV_SET(&ke[0], (uintptr_t)fd, EVFILT_READ,
+ EV_DELETE, 0, 0, UPTR(NULL));
+ if (e->write_cb)
+ EV_SET(&ke[1], (uintptr_t)fd,
+ EVFILT_WRITE, EV_DELETE, 0, 0, UPTR(NULL));
+ kevent(eloop->poll_fd, ke, e->write_cb ? 2 : 1, NULL, 0, NULL);
#elif defined(HAVE_EPOLL)
- /* NULL event is safe because we
- * rely on epoll_pwait which as added
- * after the delete without event was fixed. */
- epoll_ctl(eloop->poll_fd, EPOLL_CTL_DEL,
- fd, NULL);
-#endif
- TAILQ_INSERT_TAIL(&eloop->free_events, e, next);
- eloop->events_len--;
- }
- eloop_event_setup_fds(eloop);
- break;
- }
- }
+ /* NULL event is safe because we
+ * rely on epoll_pwait which as added
+ * after the delete without event was fixed. */
+ epoll_ctl(eloop->poll_fd, EPOLL_CTL_DEL, fd, NULL);
+#endif
+
+ eloop_event_setup_fds(eloop);
}
int
@@ -443,7 +479,7 @@ eloop_q_timeout_add_tv(struct eloop *eloop, int queue,
return -1;
}
- /* Remove existing timeout if present */
+ /* Remove existing timeout if present. */
TAILQ_FOREACH(t, &eloop->timeouts, next) {
if (t->callback == callback && t->arg == arg) {
TAILQ_REMOVE(&eloop->timeouts, t, next);
@@ -452,7 +488,7 @@ eloop_q_timeout_add_tv(struct eloop *eloop, int queue,
}
if (t == NULL) {
- /* No existing, so allocate or grab one from the free pool */
+ /* No existing, so allocate or grab one from the free pool. */
if ((t = TAILQ_FIRST(&eloop->free_timeouts))) {
TAILQ_REMOVE(&eloop->free_timeouts, t, next);
} else {
@@ -729,6 +765,7 @@ eloop_new(void)
eloop = calloc(1, sizeof(*eloop));
if (eloop) {
TAILQ_INIT(&eloop->events);
+ eloop->events_maxfd = -1;
TAILQ_INIT(&eloop->free_events);
TAILQ_INIT(&eloop->timeouts);
TAILQ_INIT(&eloop->free_timeouts);
@@ -750,6 +787,7 @@ void eloop_free(struct eloop *eloop)
if (eloop == NULL)
return;
+ free(eloop->event_fds);
while ((e = TAILQ_FIRST(&eloop->events))) {
TAILQ_REMOVE(&eloop->events, e, next);
free(e);
@@ -800,7 +838,7 @@ eloop_start(struct eloop *eloop, sigset_t *signals)
if (eloop->exitnow)
break;
- /* Run all timeouts first */
+ /* Run all timeouts first. */
if (eloop->timeout0) {
t0 = eloop->timeout0;
eloop->timeout0 = NULL;
@@ -818,7 +856,7 @@ eloop_start(struct eloop *eloop, sigset_t *signals)
timespecsub(&t->when, &now, &ts);
tsp = &ts;
} else
- /* No timeouts, so wait forever */
+ /* No timeouts, so wait forever. */
tsp = NULL;
if (tsp == NULL && eloop->events_len == 0)
@@ -894,16 +932,22 @@ eloop_start(struct eloop *eloop, sigset_t *signals)
}
#elif defined(HAVE_POLL)
if (n > 0) {
- TAILQ_FOREACH(e, &eloop->events, next) {
- if (e->pollfd->revents & POLLOUT &&
- e->write_cb != NULL)
- {
- e->write_cb(e->write_cb_arg);
- break;
+ size_t i;
+
+ for (i = 0; i < eloop->events_len; i++) {
+ if (eloop->fds[i].revents & POLLOUT) {
+ e = eloop->event_fds[eloop->fds[i].fd];
+ if (e->write_cb != NULL) {
+ e->write_cb(e->write_cb_arg);
+ break;
+ }
}
- if (e->pollfd->revents && e->read_cb != NULL) {
- e->read_cb(e->read_cb_arg);
- break;
+ if (eloop->fds[i].revents) {
+ e = eloop->event_fds[eloop->fds[i].fd];
+ if (e->read_cb != NULL) {
+ e->read_cb(e->read_cb_arg);
+ break;
+ }
}
}
}