diff options
| author | Roy Marples <roy@marples.name> | 2016-02-26 21:26:00 +0000 |
|---|---|---|
| committer | Roy Marples <roy@marples.name> | 2016-02-26 21:26:00 +0000 |
| commit | 8b4023bab4673e76c5eea27cde0a85b5f3b82380 (patch) | |
| tree | 7ccf48749e34a19fe9d3592a20b0393d03bf7c99 | |
| parent | e8d894cffcebaccdc64f5160de89e1c3c832f966 (diff) | |
| download | parpd-8b4023bab4673e76c5eea27cde0a85b5f3b82380.tar.xz | |
Sync eloop with master from dhcpcd.
| -rw-r--r-- | eloop.c | 206 |
1 files changed, 125 insertions, 81 deletions
@@ -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; + } } } } |
