|  | /*	$OpenBSD: kqueue.c,v 1.5 2002/07/10 14:41:31 art Exp $	*/ | 
|  |  | 
|  | /* | 
|  | * Copyright 2000-2002 Niels Provos <provos@citi.umich.edu> | 
|  | * All rights reserved. | 
|  | * | 
|  | * Redistribution and use in source and binary forms, with or without | 
|  | * modification, are permitted provided that the following conditions | 
|  | * are met: | 
|  | * 1. Redistributions of source code must retain the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer. | 
|  | * 2. Redistributions in binary form must reproduce the above copyright | 
|  | *    notice, this list of conditions and the following disclaimer in the | 
|  | *    documentation and/or other materials provided with the distribution. | 
|  | * 3. The name of the author may not be used to endorse or promote products | 
|  | *    derived from this software without specific prior written permission. | 
|  | * | 
|  | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR | 
|  | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | 
|  | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | 
|  | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | 
|  | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | 
|  | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
|  | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
|  | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
|  | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | 
|  | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
|  | */ | 
|  | #ifdef HAVE_CONFIG_H | 
|  | #include "config.h" | 
|  | #endif | 
|  |  | 
|  | #define _GNU_SOURCE 1 | 
|  |  | 
|  | #include <sys/types.h> | 
|  | #ifdef HAVE_SYS_TIME_H | 
|  | #include <sys/time.h> | 
|  | #else | 
|  | #include <sys/_libevent_time.h> | 
|  | #endif | 
|  | #include <sys/queue.h> | 
|  | #include <sys/event.h> | 
|  | #include <signal.h> | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  | #include <string.h> | 
|  | #include <unistd.h> | 
|  | #include <errno.h> | 
|  | #include <assert.h> | 
|  | #ifdef HAVE_INTTYPES_H | 
|  | #include <inttypes.h> | 
|  | #endif | 
|  |  | 
|  | /* Some platforms apparently define the udata field of struct kevent as | 
|  | * intptr_t, whereas others define it as void*.  There doesn't seem to be an | 
|  | * easy way to tell them apart via autoconf, so we need to use OS macros. */ | 
|  | #if defined(HAVE_INTTYPES_H) && !defined(__OpenBSD__) && !defined(__FreeBSD__) && !defined(__darwin__) && !defined(__APPLE__) | 
|  | #define PTR_TO_UDATA(x)	((intptr_t)(x)) | 
|  | #else | 
|  | #define PTR_TO_UDATA(x)	(x) | 
|  | #endif | 
|  |  | 
|  | #include "event.h" | 
|  | #include "event-internal.h" | 
|  | #include "log.h" | 
|  | #include "evsignal.h" | 
|  |  | 
|  | #define EVLIST_X_KQINKERNEL	0x1000 | 
|  |  | 
|  | #define NEVENT		64 | 
|  |  | 
|  | struct kqop { | 
|  | struct kevent *changes; | 
|  | int nchanges; | 
|  | struct kevent *events; | 
|  | struct event_list evsigevents[NSIG]; | 
|  | int nevents; | 
|  | int kq; | 
|  | pid_t pid; | 
|  | }; | 
|  |  | 
|  | static void *kq_init	(struct event_base *); | 
|  | static int kq_add	(void *, struct event *); | 
|  | static int kq_del	(void *, struct event *); | 
|  | static int kq_dispatch	(struct event_base *, void *, struct timeval *); | 
|  | static int kq_insert	(struct kqop *, struct kevent *); | 
|  | static void kq_dealloc (struct event_base *, void *); | 
|  |  | 
|  | const struct eventop kqops = { | 
|  | "kqueue", | 
|  | kq_init, | 
|  | kq_add, | 
|  | kq_del, | 
|  | kq_dispatch, | 
|  | kq_dealloc, | 
|  | 1 /* need reinit */ | 
|  | }; | 
|  |  | 
|  | static void * | 
|  | kq_init(struct event_base *base) | 
|  | { | 
|  | int i, kq; | 
|  | struct kqop *kqueueop; | 
|  |  | 
|  | /* Disable kqueue when this environment variable is set */ | 
|  | if (evutil_getenv("EVENT_NOKQUEUE")) | 
|  | return (NULL); | 
|  |  | 
|  | if (!(kqueueop = calloc(1, sizeof(struct kqop)))) | 
|  | return (NULL); | 
|  |  | 
|  | /* Initalize the kernel queue */ | 
|  |  | 
|  | if ((kq = kqueue()) == -1) { | 
|  | event_warn("kqueue"); | 
|  | free (kqueueop); | 
|  | return (NULL); | 
|  | } | 
|  |  | 
|  | kqueueop->kq = kq; | 
|  |  | 
|  | kqueueop->pid = getpid(); | 
|  |  | 
|  | /* Initalize fields */ | 
|  | kqueueop->changes = malloc(NEVENT * sizeof(struct kevent)); | 
|  | if (kqueueop->changes == NULL) { | 
|  | free (kqueueop); | 
|  | return (NULL); | 
|  | } | 
|  | kqueueop->events = malloc(NEVENT * sizeof(struct kevent)); | 
|  | if (kqueueop->events == NULL) { | 
|  | free (kqueueop->changes); | 
|  | free (kqueueop); | 
|  | return (NULL); | 
|  | } | 
|  | kqueueop->nevents = NEVENT; | 
|  |  | 
|  | /* we need to keep track of multiple events per signal */ | 
|  | for (i = 0; i < NSIG; ++i) { | 
|  | TAILQ_INIT(&kqueueop->evsigevents[i]); | 
|  | } | 
|  |  | 
|  | return (kqueueop); | 
|  | } | 
|  |  | 
|  | static int | 
|  | kq_insert(struct kqop *kqop, struct kevent *kev) | 
|  | { | 
|  | int nevents = kqop->nevents; | 
|  |  | 
|  | if (kqop->nchanges == nevents) { | 
|  | struct kevent *newchange; | 
|  | struct kevent *newresult; | 
|  |  | 
|  | nevents *= 2; | 
|  |  | 
|  | newchange = realloc(kqop->changes, | 
|  | nevents * sizeof(struct kevent)); | 
|  | if (newchange == NULL) { | 
|  | event_warn("%s: malloc", __func__); | 
|  | return (-1); | 
|  | } | 
|  | kqop->changes = newchange; | 
|  |  | 
|  | newresult = realloc(kqop->events, | 
|  | nevents * sizeof(struct kevent)); | 
|  |  | 
|  | /* | 
|  | * If we fail, we don't have to worry about freeing, | 
|  | * the next realloc will pick it up. | 
|  | */ | 
|  | if (newresult == NULL) { | 
|  | event_warn("%s: malloc", __func__); | 
|  | return (-1); | 
|  | } | 
|  | kqop->events = newresult; | 
|  |  | 
|  | kqop->nevents = nevents; | 
|  | } | 
|  |  | 
|  | memcpy(&kqop->changes[kqop->nchanges++], kev, sizeof(struct kevent)); | 
|  |  | 
|  | event_debug(("%s: fd %d %s%s", | 
|  | __func__, (int)kev->ident, | 
|  | kev->filter == EVFILT_READ ? "EVFILT_READ" : "EVFILT_WRITE", | 
|  | kev->flags == EV_DELETE ? " (del)" : "")); | 
|  |  | 
|  | return (0); | 
|  | } | 
|  |  | 
|  | static void | 
|  | kq_sighandler(int sig) | 
|  | { | 
|  | /* Do nothing here */ | 
|  | } | 
|  |  | 
|  | static int | 
|  | kq_dispatch(struct event_base *base, void *arg, struct timeval *tv) | 
|  | { | 
|  | struct kqop *kqop = arg; | 
|  | struct kevent *changes = kqop->changes; | 
|  | struct kevent *events = kqop->events; | 
|  | struct event *ev; | 
|  | struct timespec ts, *ts_p = NULL; | 
|  | int i, res; | 
|  |  | 
|  | if (tv != NULL) { | 
|  | TIMEVAL_TO_TIMESPEC(tv, &ts); | 
|  | ts_p = &ts; | 
|  | } | 
|  |  | 
|  | res = kevent(kqop->kq, changes, kqop->nchanges, | 
|  | events, kqop->nevents, ts_p); | 
|  | kqop->nchanges = 0; | 
|  | if (res == -1) { | 
|  | if (errno != EINTR) { | 
|  | event_warn("kevent"); | 
|  | return (-1); | 
|  | } | 
|  |  | 
|  | return (0); | 
|  | } | 
|  |  | 
|  | event_debug(("%s: kevent reports %d", __func__, res)); | 
|  |  | 
|  | for (i = 0; i < res; i++) { | 
|  | int which = 0; | 
|  |  | 
|  | if (events[i].flags & EV_ERROR) { | 
|  | /* | 
|  | * Error messages that can happen, when a delete fails. | 
|  | *   EBADF happens when the file discriptor has been | 
|  | *   closed, | 
|  | *   ENOENT when the file discriptor was closed and | 
|  | *   then reopened. | 
|  | *   EINVAL for some reasons not understood; EINVAL | 
|  | *   should not be returned ever; but FreeBSD does :-\ | 
|  | * An error is also indicated when a callback deletes | 
|  | * an event we are still processing.  In that case | 
|  | * the data field is set to ENOENT. | 
|  | */ | 
|  | if (events[i].data == EBADF || | 
|  | events[i].data == EINVAL || | 
|  | events[i].data == ENOENT) | 
|  | continue; | 
|  | errno = events[i].data; | 
|  | return (-1); | 
|  | } | 
|  |  | 
|  | if (events[i].filter == EVFILT_READ) { | 
|  | which |= EV_READ; | 
|  | } else if (events[i].filter == EVFILT_WRITE) { | 
|  | which |= EV_WRITE; | 
|  | } else if (events[i].filter == EVFILT_SIGNAL) { | 
|  | which |= EV_SIGNAL; | 
|  | } | 
|  |  | 
|  | if (!which) | 
|  | continue; | 
|  |  | 
|  | if (events[i].filter == EVFILT_SIGNAL) { | 
|  | struct event_list *head = | 
|  | (struct event_list *)events[i].udata; | 
|  | TAILQ_FOREACH(ev, head, ev_signal_next) { | 
|  | event_active(ev, which, events[i].data); | 
|  | } | 
|  | } else { | 
|  | ev = (struct event *)events[i].udata; | 
|  |  | 
|  | if (!(ev->ev_events & EV_PERSIST)) | 
|  | ev->ev_flags &= ~EVLIST_X_KQINKERNEL; | 
|  |  | 
|  | event_active(ev, which, 1); | 
|  | } | 
|  | } | 
|  |  | 
|  | return (0); | 
|  | } | 
|  |  | 
|  |  | 
|  | static int | 
|  | kq_add(void *arg, struct event *ev) | 
|  | { | 
|  | struct kqop *kqop = arg; | 
|  | struct kevent kev; | 
|  |  | 
|  | if (ev->ev_events & EV_SIGNAL) { | 
|  | int nsignal = EVENT_SIGNAL(ev); | 
|  |  | 
|  | assert(nsignal >= 0 && nsignal < NSIG); | 
|  | if (TAILQ_EMPTY(&kqop->evsigevents[nsignal])) { | 
|  | struct timespec timeout = { 0, 0 }; | 
|  |  | 
|  | memset(&kev, 0, sizeof(kev)); | 
|  | kev.ident = nsignal; | 
|  | kev.filter = EVFILT_SIGNAL; | 
|  | kev.flags = EV_ADD; | 
|  | kev.udata = PTR_TO_UDATA(&kqop->evsigevents[nsignal]); | 
|  |  | 
|  | /* Be ready for the signal if it is sent any | 
|  | * time between now and the next call to | 
|  | * kq_dispatch. */ | 
|  | if (kevent(kqop->kq, &kev, 1, NULL, 0, &timeout) == -1) | 
|  | return (-1); | 
|  |  | 
|  | if (_evsignal_set_handler(ev->ev_base, nsignal, | 
|  | kq_sighandler) == -1) | 
|  | return (-1); | 
|  | } | 
|  |  | 
|  | TAILQ_INSERT_TAIL(&kqop->evsigevents[nsignal], ev, | 
|  | ev_signal_next); | 
|  | ev->ev_flags |= EVLIST_X_KQINKERNEL; | 
|  | return (0); | 
|  | } | 
|  |  | 
|  | if (ev->ev_events & EV_READ) { | 
|  | memset(&kev, 0, sizeof(kev)); | 
|  | kev.ident = ev->ev_fd; | 
|  | kev.filter = EVFILT_READ; | 
|  | #ifdef NOTE_EOF | 
|  | /* Make it behave like select() and poll() */ | 
|  | kev.fflags = NOTE_EOF; | 
|  | #endif | 
|  | kev.flags = EV_ADD; | 
|  | if (!(ev->ev_events & EV_PERSIST)) | 
|  | kev.flags |= EV_ONESHOT; | 
|  | kev.udata = PTR_TO_UDATA(ev); | 
|  |  | 
|  | if (kq_insert(kqop, &kev) == -1) | 
|  | return (-1); | 
|  |  | 
|  | ev->ev_flags |= EVLIST_X_KQINKERNEL; | 
|  | } | 
|  |  | 
|  | if (ev->ev_events & EV_WRITE) { | 
|  | memset(&kev, 0, sizeof(kev)); | 
|  | kev.ident = ev->ev_fd; | 
|  | kev.filter = EVFILT_WRITE; | 
|  | kev.flags = EV_ADD; | 
|  | if (!(ev->ev_events & EV_PERSIST)) | 
|  | kev.flags |= EV_ONESHOT; | 
|  | kev.udata = PTR_TO_UDATA(ev); | 
|  |  | 
|  | if (kq_insert(kqop, &kev) == -1) | 
|  | return (-1); | 
|  |  | 
|  | ev->ev_flags |= EVLIST_X_KQINKERNEL; | 
|  | } | 
|  |  | 
|  | return (0); | 
|  | } | 
|  |  | 
|  | static int | 
|  | kq_del(void *arg, struct event *ev) | 
|  | { | 
|  | struct kqop *kqop = arg; | 
|  | struct kevent kev; | 
|  |  | 
|  | if (!(ev->ev_flags & EVLIST_X_KQINKERNEL)) | 
|  | return (0); | 
|  |  | 
|  | if (ev->ev_events & EV_SIGNAL) { | 
|  | int nsignal = EVENT_SIGNAL(ev); | 
|  | struct timespec timeout = { 0, 0 }; | 
|  |  | 
|  | assert(nsignal >= 0 && nsignal < NSIG); | 
|  | TAILQ_REMOVE(&kqop->evsigevents[nsignal], ev, ev_signal_next); | 
|  | if (TAILQ_EMPTY(&kqop->evsigevents[nsignal])) { | 
|  | memset(&kev, 0, sizeof(kev)); | 
|  | kev.ident = nsignal; | 
|  | kev.filter = EVFILT_SIGNAL; | 
|  | kev.flags = EV_DELETE; | 
|  |  | 
|  | /* Because we insert signal events | 
|  | * immediately, we need to delete them | 
|  | * immediately, too */ | 
|  | if (kevent(kqop->kq, &kev, 1, NULL, 0, &timeout) == -1) | 
|  | return (-1); | 
|  |  | 
|  | if (_evsignal_restore_handler(ev->ev_base, | 
|  | nsignal) == -1) | 
|  | return (-1); | 
|  | } | 
|  |  | 
|  | ev->ev_flags &= ~EVLIST_X_KQINKERNEL; | 
|  | return (0); | 
|  | } | 
|  |  | 
|  | if (ev->ev_events & EV_READ) { | 
|  | memset(&kev, 0, sizeof(kev)); | 
|  | kev.ident = ev->ev_fd; | 
|  | kev.filter = EVFILT_READ; | 
|  | kev.flags = EV_DELETE; | 
|  |  | 
|  | if (kq_insert(kqop, &kev) == -1) | 
|  | return (-1); | 
|  |  | 
|  | ev->ev_flags &= ~EVLIST_X_KQINKERNEL; | 
|  | } | 
|  |  | 
|  | if (ev->ev_events & EV_WRITE) { | 
|  | memset(&kev, 0, sizeof(kev)); | 
|  | kev.ident = ev->ev_fd; | 
|  | kev.filter = EVFILT_WRITE; | 
|  | kev.flags = EV_DELETE; | 
|  |  | 
|  | if (kq_insert(kqop, &kev) == -1) | 
|  | return (-1); | 
|  |  | 
|  | ev->ev_flags &= ~EVLIST_X_KQINKERNEL; | 
|  | } | 
|  |  | 
|  | return (0); | 
|  | } | 
|  |  | 
|  | static void | 
|  | kq_dealloc(struct event_base *base, void *arg) | 
|  | { | 
|  | struct kqop *kqop = arg; | 
|  |  | 
|  | evsignal_dealloc(base); | 
|  |  | 
|  | if (kqop->changes) | 
|  | free(kqop->changes); | 
|  | if (kqop->events) | 
|  | free(kqop->events); | 
|  | if (kqop->kq >= 0 && kqop->pid == getpid()) | 
|  | close(kqop->kq); | 
|  |  | 
|  | memset(kqop, 0, sizeof(struct kqop)); | 
|  | free(kqop); | 
|  | } |