import of libevent; an event abstraction library that supports kqueue and

poll on NetBSD.
This commit is contained in:
provos 2003-06-12 22:54:25 +00:00
parent 6933a49a87
commit 13993283c2
10 changed files with 2023 additions and 0 deletions

32
lib/libevent/Makefile Normal file
View File

@ -0,0 +1,32 @@
# $NetBSD: Makefile,v 1.1.1.1 2003/06/12 22:54:25 provos Exp $
# @(#)Makefile 8.1 (Berkeley) 6/4/93
NOLINT= # Until someone explains to me how to avoid lint stupidity
USE_SHLIBDIR= yes
CPPFLAGS+=-DHAVE_CONFIG_H
.include <bsd.own.mk>
LIB= event
SRCS= event.c kqueue.c poll.c signal.c
INCS= event.h
INCSDIR=/usr/include
MAN= event.3
MLINKS+=event.3 event_init.3
MLINKS+=event.3 event_dispatch.3
MLINKS+=event.3 event_loop.3
MLINKS+=event.3 event_set.3
MLINKS+=event.3 event_add.3
MLINKS+=event.3 event_del.3
MLINKS+=event.3 event_pending.3
MLINKS+=event.3 event_initialized.3
MLINKS+=event.3 signal_set.3
MLINKS+=event.3 signal_add.3
MLINKS+=event.3 signal_del.3
MLINKS+=event.3 signal_pending.3
MLINKS+=event.3 signal_initialized.3
.include <bsd.lib.mk>

150
lib/libevent/config.h Normal file
View File

@ -0,0 +1,150 @@
/* config.h. Generated by configure. */
/* config.h.in. Generated from configure.in by autoheader. */
/* Define if kqueue works correctly with pipes */
#define HAVE_WORKING_KQUEUE 1
/* Define to `unsigned long long' if <sys/types.h> doesn't define. */
/* #undef u_int64_t */
/* Define to `unsigned int' if <sys/types.h> doesn't define. */
/* #undef u_int32_t */
/* Define to `unsigned short' if <sys/types.h> doesn't define. */
/* #undef u_int16_t */
/* Define to `unsigned char' if <sys/types.h> doesn't define. */
/* #undef u_int8_t */
/* Define if timeradd is defined in <sys/time.h> */
#define HAVE_TIMERADD 1
#ifndef HAVE_TIMERADD
#define timeradd(tvp, uvp, vvp) \
do { \
(vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \
(vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \
if ((vvp)->tv_usec >= 1000000) { \
(vvp)->tv_sec++; \
(vvp)->tv_usec -= 1000000; \
} \
} while (0)
#define timersub(tvp, uvp, vvp) \
do { \
(vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \
(vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \
if ((vvp)->tv_usec < 0) { \
(vvp)->tv_sec--; \
(vvp)->tv_usec += 1000000; \
} \
} while (0)
#endif /* !HAVE_TIMERADD */
/* Define if TAILQ_FOREACH is defined in <sys/queue.h> */
#define HAVE_TAILQFOREACH 1
#ifndef HAVE_TAILQFOREACH
#define TAILQ_FIRST(head) ((head)->tqh_first)
#define TAILQ_END(head) NULL
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
#define TAILQ_FOREACH(var, head, field) \
for((var) = TAILQ_FIRST(head); \
(var) != TAILQ_END(head); \
(var) = TAILQ_NEXT(var, field))
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
(elm)->field.tqe_next = (listelm); \
*(listelm)->field.tqe_prev = (elm); \
(listelm)->field.tqe_prev = &(elm)->field.tqe_next; \
} while (0)
#endif /* TAILQ_FOREACH */
/* Define to 1 if you have the `err' function. */
#define HAVE_ERR 1
/* Define to 1 if you have the `gettimeofday' function. */
#define HAVE_GETTIMEOFDAY 1
/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1
/* Define to 1 if you have the `kqueue' function. */
#define HAVE_KQUEUE 1
/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1
/* Define to 1 if you have the `poll' function. */
#define HAVE_POLL 1
/* Define to 1 if you have the <poll.h> header file. */
#define HAVE_POLL_H 1
/* Define to 1 if you have the `select' function. */
#undef HAVE_SELECT
/* Define to 1 if you have the <signal.h> header file. */
#define HAVE_SIGNAL_H 1
/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
/* Define to 1 if you have the <strings.h> header file. */
#define HAVE_STRINGS_H 1
/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1
/* Define to 1 if you have the <sys/event.h> header file. */
#define HAVE_SYS_EVENT_H 1
/* Define to 1 if you have the <sys/queue.h> header file. */
#define HAVE_SYS_QUEUE_H 1
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1
/* Define to 1 if you have the <sys/time.h> header file. */
#define HAVE_SYS_TIME_H 1
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
/* Define if TAILQ_FOREACH is defined in <sys/queue.h> */
#define HAVE_TAILQFOREACH 1
/* Define if timeradd is defined in <sys/time.h> */
#define HAVE_TIMERADD 1
/* Define to 1 if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 1
/* Define if kqueue works correctly with pipes */
#define HAVE_WORKING_KQUEUE 1
/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1
/* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
#define TIME_WITH_SYS_TIME 1
/* Define to `int' if <sys/types.h> does not define. */
/* #undef pid_t */
/* Define to `unsigned' if <sys/types.h> does not define. */
/* #undef size_t */
/* Define to unsigned int if you dont have it */
/* #undef socklen_t */
/* Define to `unsigned short' if <sys/types.h> does not define. */
/* #undef u_int16_t */
/* Define to `unsigned int' if <sys/types.h> does not define. */
/* #undef u_int32_t */
/* Define to `unsigned long long' if <sys/types.h> does not define. */
/* #undef u_int64_t */
/* Define to `unsigned char' if <sys/types.h> does not define. */
/* #undef u_int8_t */

316
lib/libevent/event.3 Normal file
View File

@ -0,0 +1,316 @@
.\" $OpenBSD: event.3,v 1.4 2002/07/12 18:50:48 provos Exp $
.\"
.\" Copyright (c) 2000 Artur Grabowski <art@openbsd.org>
.\" 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 ``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.
.\"
.Dd August 8, 2000
.Dt EVENT 3
.Os
.Sh NAME
.Nm event_init ,
.Nm event_dispatch ,
.Nm event_loop ,
.Nm event_set ,
.Nm event_add ,
.Nm event_del ,
.Nm event_pending ,
.Nm event_initialized ,
.Nm evtimer_set ,
.Nm evtimer_add ,
.Nm evtimer_del
.Nm evtimer_pending ,
.Nm evtimer_initialized ,
.Nm signal_set ,
.Nm signal_add ,
.Nm signal_del
.Nm signal_pending ,
.Nm signal_initialized
.Nd execute a function when a specific event occurs
.Sh SYNOPSIS
.Fd #include <sys/time.h>
.Fd #include <event.h>
.Ft void
.Fn "event_init"
.Ft int
.Fn "event_dispatch"
.Ft int
.Fn "event_loop" "int flags"
.Ft void
.Fn "event_set" "struct event *ev" "int fd" "short event" "void (*fn)(int, short, void *)" "void *arg"
.Ft int
.Fn "event_add" "struct event *ev" "struct timeval *tv"
.Ft int
.Fn "event_del" "struct event *ev"
.Ft int
.Fn "event_pending" "struct event *ev" "short event" "struct timeval *tv"
.Ft int
.Fn "event_initialized" "struct event *ev"
.Ft void
.Fn "evtimer_set" "struct event *ev" "void (*fn)(int, short, void *)" "void *arg"
.Ft void
.Fn "evtimer_add" "struct event *ev" "struct timeval *"
.Ft void
.Fn "evtimer_del" "struct event *ev"
.Ft int
.Fn "evtimer_pending" "struct event *ev" "struct timeval *tv"
.Ft int
.Fn "evtimer_initialized" "struct event *ev"
.Ft void
.Fn "signal_set" "struct event *ev" "int signal" "void (*fn)(int, short, void *)" "void *arg"
.Ft void
.Fn "signal_add" "struct event *ev" "struct timeval *"
.Ft void
.Fn "signal_del" "struct event *ev"
.Ft int
.Fn "signal_pending" "struct event *ev" "struct timeval *tv"
.Ft int
.Fn "signal_initialized" "struct event *ev"
.Ft int
.Fa (*event_sigcb)(void) ;
.Ft int
.Fa event_gotsig ;
.Sh DESCRIPTION
The
.Nm event
API provides a mechanism to execute a function when a specific event
on a file descriptor occurs or after a given time has passed.
.Pp
The
.Nm event
API needs to be initialized with
.Fn event_init
before it can be used.
.Pp
In order to process events, an application needs to call
.Fn event_dispatch .
This function only returns on error, and should replace the event core
of the application program.
.Pp
In order to avoid races in signal handlers, the
.Nm event
API provides two variables:
.Va event_sigcb
and
.Va event_gotsig .
A signal handler
sets
.Va event_gotsig
to indicate that a signal has been received.
The application sets
.Va event_sigcb
to a callback function. After the signal handler sets
.Va event_gotsig ,
.Nm event_dispatch
will execute the callback function to process received signals. The
callback returns 1 when no events are registered any more. It can
return -1 to indicate an error to the
.Nm event
library, causing
.Fn event_dispatch
to terminate with
.Va errno
set to
.Er EINTR.
.Pp
The
.Nm event_loop
function provides an interface for single pass execution of pending
events. The flags
.Va EVLOOP_ONCE
and
.Va EVLOOP_NONBLOCK
are recognized.
.Pp
It is the responsibility of the caller to provide these functions with
pre-allocated event structures.
.Pp
The function
.Fn event_set
prepares the event structure
.Fa ev
to be used in future calls to
.Fn event_add
and
.Fn event_del .
The event will be prepared to call the function specified by the
.Fa fn
argument with an
.Fa int
argument indicating the file descriptor, a
.Fa short
argument indicating the type of event, and a
.Fa void *
argument given in the
.Fa arg
argument.
The
.Fa fd
indicates the file descriptor that should be monitored for events.
The events can be either
.Va EV_READ ,
.Va EV_WRITE ,
or both.
Indicating that an application can read or write from the file descriptor
respectively without blocking.
.Pp
The function
.Fa fn
will be called with the file descriptor that triggered the event and
the type of event which will be either
.Va EV_TIMEOUT ,
.Va EV_SIGNAL ,
.Va EV_READ ,
or
.Va EV_WRITE .
The additional flag
.Va EV_PERSIST
makes an
.Fn event_add
persistent until
.Fn event_del
has been called.
.Pp
Once initialized, the
.Fa ev
structure can be used repeatedly with
.Fn event_add
and
.Fn event_del
and does not need to be reinitialized unless the function called and/or
the argument to it are to be changed.
.Pp
The function
.Fn event_add
schedules the execution of the
.Fa ev
event when the event specified in
.Fn event_set
occurs or in at least the time specified in the
.Fa tv .
If
.Fa tv
is NULL, no timeout occurs and the function will only be called
if a matching event occurs on the file descriptor.
The event in the
.Fa ev
argument must be already initialized by
.Fn event_set
and may not be used in calls to
.Fn event_set
until it has timed out or been removed with
.Fn event_del .
If the event in the
.Fa ev
argument already has a scheduled timeout, the old timeout will be
replaced by the new one.
.Pp
The function
.Fn event_del
will cancel the event in the argument
.Fa ev .
If the event has already executed or has never been added
the call will have no effect.
.Pp
The
.Fn event_pending
function can be used to check if the event specified by
.Fa event
is pending to run.
If
.Va EV_TIMEOUT
was specified and
.Fa tv
is not
.Va NULL ,
the expiration time of the event will be returned in
.Fa tv .
.Pp
The
.Fn event_initialized
macro can be used to check if an event has been initialized.
.Pp
The functions
.Fn evtimer_set ,
.Fn evtimer_add ,
.Fn evtimer_del ,
.Fn evtimer_initialized ,
and
.Fn evtimer_pending
are abbreviations for common situations where only a timeout is required.
The file descriptor passed will be 0, and the event type will be
.Va EV_TIMEOUT .
.Pp
.Pp
The functions
.Fn signal_set ,
.Fn signal_add ,
.Fn signal_del ,
.Fn signal_initialized ,
and
.Fn signal_pending
are abbreviations.
The event type will be a persistent
.Va EV_SIGNAL .
That means
.Fn signal_set
adds
.Va EV_PERSIST .
.Pp
It is possible to disable support for
.Va epoll , kqueue , poll
or
.Va select
by setting the environment variable
.Va EVENT_NOEPOLL , EVENT_NOKQUEUE , EVENT_NOPOLL
or
.Va EVENT_NOSELECT .
By setting the environment variable
.Va EVENT_SHOW_METHOD ,
.Nm libevent
displays the kernel notification method that it uses.
.Pp
.Sh RETURN VALUES
Upon successful completion
.Fn event_add
and
.Fn event_del
return 0.
Otherwise, -1 is returned and the global variable errno is
set to indicate the error.
.Sh SEE ALSO
.Xr timeout 9 ,
.Xr select 2 ,
.Xr kqueue 2
.Sh HISTORY
The
.Nm event
API manpage is based on the
.Xr timeout 9
manpage by Artur Grabowski.
.Sh AUTHORS
The
.Nm event
library was written by Niels Provos.

538
lib/libevent/event.c Normal file
View File

@ -0,0 +1,538 @@
/* $OpenBSD: event.c,v 1.2 2002/06/25 15:50:15 mickey 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Niels Provos.
* 4. 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
#include <sys/types.h>
#include <sys/tree.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#include <sys/_time.h>
#endif
#include <sys/queue.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <err.h>
#include <assert.h>
#ifdef USE_LOG
#include "log.h"
#else
#define LOG_DBG(x)
#define log_error(x) perror(x)
#endif
#include "event.h"
#ifdef HAVE_SELECT
extern struct eventop selectops;
#endif
#ifdef HAVE_POLL
extern struct eventop pollops;
#endif
#ifdef HAVE_EPOLL
extern struct eventop epollops;
#endif
#ifdef HAVE_WORKING_KQUEUE
extern struct eventop kqops;
#endif
/* In order of preference */
struct eventop *eventops[] = {
#ifdef HAVE_WORKING_KQUEUE
&kqops,
#endif
#ifdef HAVE_EPOLL
&epollops,
#endif
#ifdef HAVE_POLL
&pollops,
#endif
#ifdef HAVE_SELECT
&selectops,
#endif
NULL
};
struct eventop *evsel;
void *evbase;
/* Handle signals */
int (*event_sigcb)(void); /* Signal callback when gotsig is set */
int event_gotsig; /* Set in signal handler */
/* Prototypes */
void event_queue_insert(struct event *, int);
void event_queue_remove(struct event *, int);
static RB_HEAD(event_tree, event) timetree;
static struct event_list activequeue;
struct event_list signalqueue;
struct event_list eventqueue;
static struct timeval event_tv;
static int
compare(struct event *a, struct event *b)
{
if (timercmp(&a->ev_timeout, &b->ev_timeout, <))
return (-1);
else if (timercmp(&a->ev_timeout, &b->ev_timeout, >))
return (1);
return (0);
}
RB_PROTOTYPE(event_tree, event, ev_timeout_node, compare);
RB_GENERATE(event_tree, event, ev_timeout_node, compare);
void
event_init(void)
{
int i;
event_sigcb = NULL;
event_gotsig = 0;
gettimeofday(&event_tv, NULL);
RB_INIT(&timetree);
TAILQ_INIT(&eventqueue);
TAILQ_INIT(&activequeue);
TAILQ_INIT(&signalqueue);
evbase = NULL;
for (i = 0; eventops[i] && !evbase; i++) {
evsel = eventops[i];
evbase = evsel->init();
}
if (evbase == NULL)
errx(1, "%s: no event mechanism available", __func__);
if (!issetugid() && getenv("EVENT_SHOW_METHOD"))
fprintf(stderr, "libevent using: %s\n", evsel->name);
#if defined(USE_LOG) && defined(USE_DEBUG)
log_to(stderr);
log_debug_cmd(LOG_MISC, 80);
#endif
}
int
event_haveevents(void)
{
return (RB_ROOT(&timetree) || TAILQ_FIRST(&eventqueue) ||
TAILQ_FIRST(&signalqueue) || TAILQ_FIRST(&activequeue));
}
void
event_process_active(void)
{
struct event *ev;
short ncalls;
for (ev = TAILQ_FIRST(&activequeue); ev;
ev = TAILQ_FIRST(&activequeue)) {
event_queue_remove(ev, EVLIST_ACTIVE);
/* Allows deletes to work */
ncalls = ev->ev_ncalls;
ev->ev_pncalls = &ncalls;
while (ncalls) {
ncalls--;
ev->ev_ncalls = ncalls;
(*ev->ev_callback)(ev->ev_fd, ev->ev_res, ev->ev_arg);
}
}
}
int
event_dispatch(void)
{
return (event_loop(0));
}
int
event_loop(int flags)
{
struct timeval tv;
int res, done;
/* Calculate the initial events that we are waiting for */
if (evsel->recalc(evbase, 0) == -1)
return (-1);
done = 0;
while (!done) {
while (event_gotsig) {
event_gotsig = 0;
if (event_sigcb) {
res = (*event_sigcb)();
if (res == -1) {
errno = EINTR;
return (-1);
}
}
}
/* Check if time is running backwards */
gettimeofday(&tv, NULL);
if (timercmp(&tv, &event_tv, <)) {
struct timeval off;
LOG_DBG((LOG_MIST, 10,
"%s: time is running backwards, corrected",
__func__));
timersub(&event_tv, &tv, &off);
timeout_correct(&off);
}
event_tv = tv;
if (!(flags & EVLOOP_NONBLOCK))
timeout_next(&tv);
else
timerclear(&tv);
/* If we have no events, we just exit */
if (!event_haveevents())
return (1);
res = evsel->dispatch(evbase, &tv);
if (res == -1)
return (-1);
timeout_process();
if (TAILQ_FIRST(&activequeue)) {
event_process_active();
if (flags & EVLOOP_ONCE)
done = 1;
} else if (flags & EVLOOP_NONBLOCK)
done = 1;
if (evsel->recalc(evbase, 0) == -1)
return (-1);
}
return (0);
}
void
event_set(struct event *ev, int fd, short events,
void (*callback)(int, short, void *), void *arg)
{
ev->ev_callback = callback;
ev->ev_arg = arg;
ev->ev_fd = fd;
ev->ev_events = events;
ev->ev_flags = EVLIST_INIT;
ev->ev_ncalls = 0;
ev->ev_pncalls = NULL;
}
/*
* Checks if a specific event is pending or scheduled.
*/
int
event_pending(struct event *ev, short event, struct timeval *tv)
{
int flags = 0;
if (ev->ev_flags & EVLIST_INSERTED)
flags |= (ev->ev_events & (EV_READ|EV_WRITE));
if (ev->ev_flags & EVLIST_ACTIVE)
flags |= ev->ev_res;
if (ev->ev_flags & EVLIST_TIMEOUT)
flags |= EV_TIMEOUT;
event &= (EV_TIMEOUT|EV_READ|EV_WRITE);
/* See if there is a timeout that we should report */
if (tv != NULL && (flags & event & EV_TIMEOUT))
*tv = ev->ev_timeout;
return (flags & event);
}
int
event_add(struct event *ev, struct timeval *tv)
{
LOG_DBG((LOG_MISC, 55,
"event_add: event: %p, %s%s%scall %p",
ev,
ev->ev_events & EV_READ ? "EV_READ " : " ",
ev->ev_events & EV_WRITE ? "EV_WRITE " : " ",
tv ? "EV_TIMEOUT " : " ",
ev->ev_callback));
assert(!(ev->ev_flags & ~EVLIST_ALL));
if (tv != NULL) {
struct timeval now;
if (ev->ev_flags & EVLIST_TIMEOUT)
event_queue_remove(ev, EVLIST_TIMEOUT);
/* Check if it is active due to a timeout. Rescheduling
* this timeout before the callback can be executed
* removes it from the active list. */
if ((ev->ev_flags & EVLIST_ACTIVE) &&
(ev->ev_res & EV_TIMEOUT)) {
/* See if we are just active executing this
* event in a loop
*/
if (ev->ev_ncalls && ev->ev_pncalls) {
/* Abort loop */
*ev->ev_pncalls = 0;
}
event_queue_remove(ev, EVLIST_ACTIVE);
}
gettimeofday(&now, NULL);
timeradd(&now, tv, &ev->ev_timeout);
LOG_DBG((LOG_MISC, 55,
"event_add: timeout in %d seconds, call %p",
tv->tv_sec, ev->ev_callback));
event_queue_insert(ev, EVLIST_TIMEOUT);
}
if ((ev->ev_events & (EV_READ|EV_WRITE)) &&
!(ev->ev_flags & (EVLIST_INSERTED|EVLIST_ACTIVE))) {
event_queue_insert(ev, EVLIST_INSERTED);
return (evsel->add(evbase, ev));
} else if ((ev->ev_events & EV_SIGNAL) &&
!(ev->ev_flags & EVLIST_SIGNAL)) {
event_queue_insert(ev, EVLIST_SIGNAL);
return (evsel->add(evbase, ev));
}
return (0);
}
int
event_del(struct event *ev)
{
LOG_DBG((LOG_MISC, 80, "event_del: %p, callback %p",
ev, ev->ev_callback));
assert(!(ev->ev_flags & ~EVLIST_ALL));
/* See if we are just active executing this event in a loop */
if (ev->ev_ncalls && ev->ev_pncalls) {
/* Abort loop */
*ev->ev_pncalls = 0;
}
if (ev->ev_flags & EVLIST_TIMEOUT)
event_queue_remove(ev, EVLIST_TIMEOUT);
if (ev->ev_flags & EVLIST_ACTIVE)
event_queue_remove(ev, EVLIST_ACTIVE);
if (ev->ev_flags & EVLIST_INSERTED) {
event_queue_remove(ev, EVLIST_INSERTED);
return (evsel->del(evbase, ev));
} else if (ev->ev_flags & EVLIST_SIGNAL) {
event_queue_remove(ev, EVLIST_SIGNAL);
return (evsel->del(evbase, ev));
}
return (0);
}
void
event_active(struct event *ev, int res, short ncalls)
{
/* We get different kinds of events, add them together */
if (ev->ev_flags & EVLIST_ACTIVE) {
ev->ev_res |= res;
return;
}
ev->ev_res = res;
ev->ev_ncalls = ncalls;
ev->ev_pncalls = NULL;
event_queue_insert(ev, EVLIST_ACTIVE);
}
int
timeout_next(struct timeval *tv)
{
struct timeval dflt = TIMEOUT_DEFAULT;
struct timeval now;
struct event *ev;
if ((ev = RB_MIN(event_tree, &timetree)) == NULL) {
*tv = dflt;
return (0);
}
if (gettimeofday(&now, NULL) == -1)
return (-1);
if (timercmp(&ev->ev_timeout, &now, <=)) {
timerclear(tv);
return (0);
}
timersub(&ev->ev_timeout, &now, tv);
LOG_DBG((LOG_MISC, 60, "timeout_next: in %d seconds", tv->tv_sec));
return (0);
}
void
timeout_correct(struct timeval *off)
{
struct event *ev;
/* We can modify the key element of the node without destroying
* the key, beause we apply it to all in the right order.
*/
RB_FOREACH(ev, event_tree, &timetree)
timersub(&ev->ev_timeout, off, &ev->ev_timeout);
}
void
timeout_process(void)
{
struct timeval now;
struct event *ev, *next;
gettimeofday(&now, NULL);
for (ev = RB_MIN(event_tree, &timetree); ev; ev = next) {
if (timercmp(&ev->ev_timeout, &now, >))
break;
next = RB_NEXT(event_tree, &timetree, ev);
event_queue_remove(ev, EVLIST_TIMEOUT);
/* delete this event from the I/O queues */
event_del(ev);
LOG_DBG((LOG_MISC, 60, "timeout_process: call %p",
ev->ev_callback));
event_active(ev, EV_TIMEOUT, 1);
}
}
void
timeout_insert(struct event *ev)
{
struct event *tmp;
tmp = RB_FIND(event_tree, &timetree, ev);
if (tmp != NULL) {
struct timeval tv;
struct timeval add = {0,1};
/* Find unique time */
tv = ev->ev_timeout;
do {
timeradd(&tv, &add, &tv);
tmp = RB_NEXT(event_tree, &timetree, tmp);
} while (tmp != NULL && timercmp(&tmp->ev_timeout, &tv, ==));
ev->ev_timeout = tv;
}
tmp = RB_INSERT(event_tree, &timetree, ev);
assert(tmp == NULL);
}
void
event_queue_remove(struct event *ev, int queue)
{
if (!(ev->ev_flags & queue))
errx(1, "%s: %p(fd %d) not on queue %x", __func__,
ev, ev->ev_fd, queue);
ev->ev_flags &= ~queue;
switch (queue) {
case EVLIST_ACTIVE:
TAILQ_REMOVE(&activequeue, ev, ev_active_next);
break;
case EVLIST_SIGNAL:
TAILQ_REMOVE(&signalqueue, ev, ev_signal_next);
break;
case EVLIST_TIMEOUT:
RB_REMOVE(event_tree, &timetree, ev);
break;
case EVLIST_INSERTED:
TAILQ_REMOVE(&eventqueue, ev, ev_next);
break;
default:
errx(1, "%s: unknown queue %x", __func__, queue);
}
}
void
event_queue_insert(struct event *ev, int queue)
{
if (ev->ev_flags & queue)
errx(1, "%s: %p(fd %d) already on queue %x", __func__,
ev, ev->ev_fd, queue);
ev->ev_flags |= queue;
switch (queue) {
case EVLIST_ACTIVE:
TAILQ_INSERT_TAIL(&activequeue, ev, ev_active_next);
break;
case EVLIST_SIGNAL:
TAILQ_INSERT_TAIL(&signalqueue, ev, ev_signal_next);
break;
case EVLIST_TIMEOUT:
timeout_insert(ev);
break;
case EVLIST_INSERTED:
TAILQ_INSERT_TAIL(&eventqueue, ev, ev_next);
break;
default:
errx(1, "%s: unknown queue %x", __func__, queue);
}
}

162
lib/libevent/event.h Normal file
View File

@ -0,0 +1,162 @@
/* $OpenBSD: event.h,v 1.4 2002/07/12 18:50:48 provos 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Niels Provos.
* 4. 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.
*/
#ifndef _EVENT_H_
#define _EVENT_H_
#ifdef __cplusplus
extern "C" {
#endif
#define EVLIST_TIMEOUT 0x01
#define EVLIST_INSERTED 0x02
#define EVLIST_SIGNAL 0x04
#define EVLIST_ACTIVE 0x08
#define EVLIST_INIT 0x80
/* EVLIST_X_ Private space: 0x1000-0xf000 */
#define EVLIST_ALL (0xf000 | 0x8f)
#define EV_TIMEOUT 0x01
#define EV_READ 0x02
#define EV_WRITE 0x04
#define EV_SIGNAL 0x08
#define EV_PERSIST 0x10 /* Persistant event */
/* Fix so that ppl dont have to run with <sys/queue.h> */
#ifndef TAILQ_ENTRY
#define _EVENT_DEFINED_TQENTRY
#define TAILQ_ENTRY(type) \
struct { \
struct type *tqe_next; /* next element */ \
struct type **tqe_prev; /* address of previous next element */ \
}
#endif /* !TAILQ_ENTRY */
#ifndef RB_ENTRY
#define _EVENT_DEFINED_RBENTRY
#define RB_ENTRY(type) \
struct { \
struct type *rbe_left; /* left element */ \
struct type *rbe_right; /* right element */ \
struct type *rbe_parent; /* parent element */ \
int rbe_color; /* node color */ \
}
#endif /* !RB_ENTRY */
struct event {
TAILQ_ENTRY (event) ev_next;
TAILQ_ENTRY (event) ev_active_next;
TAILQ_ENTRY (event) ev_signal_next;
RB_ENTRY (event) ev_timeout_node;
int ev_fd;
short ev_events;
short ev_ncalls;
short *ev_pncalls; /* Allows deletes in callback */
struct timeval ev_timeout;
void (*ev_callback)(int, short, void *arg);
void *ev_arg;
int ev_res; /* result passed to event callback */
int ev_flags;
};
#define EVENT_SIGNAL(ev) ev->ev_fd
#define EVENT_FD(ev) ev->ev_fd
#ifdef _EVENT_DEFINED_TQENTRY
#undef TAILQ_ENTRY
#undef _EVENT_DEFINED_TQENTRY
#else
TAILQ_HEAD (event_list, event);
#endif /* _EVENT_DEFINED_TQENTRY */
#ifdef _EVENT_DEFINED_RBENTRY
#undef RB_ENTRY
#undef _EVENT_DEFINED_RBENTRY
#endif /* _EVENT_DEFINED_RBENTRY */
struct eventop {
char *name;
void *(*init)(void);
int (*add)(void *, struct event *);
int (*del)(void *, struct event *);
int (*recalc)(void *, int);
int (*dispatch)(void *, struct timeval *);
};
#define TIMEOUT_DEFAULT {5, 0}
void event_init(void);
int event_dispatch(void);
#define EVLOOP_ONCE 0x01
#define EVLOOP_NONBLOCK 0x02
int event_loop(int);
int timeout_next(struct timeval *);
void timeout_correct(struct timeval *);
void timeout_process(void);
#define evtimer_add(ev, tv) event_add(ev, tv)
#define evtimer_set(ev, cb, arg) event_set(ev, -1, 0, cb, arg)
#define evtimer_del(ev) event_del(ev)
#define evtimer_pending(ev, tv) event_pending(ev, EV_TIMEOUT, tv)
#define evtimer_initialized(ev) ((ev)->ev_flags & EVLIST_INIT)
#define timeout_add(ev, tv) event_add(ev, tv)
#define timeout_set(ev, cb, arg) event_set(ev, -1, 0, cb, arg)
#define timeout_del(ev) event_del(ev)
#define timeout_pending(ev, tv) event_pending(ev, EV_TIMEOUT, tv)
#define timeout_initialized(ev) ((ev)->ev_flags & EVLIST_INIT)
#define signal_add(ev, tv) event_add(ev, tv)
#define signal_set(ev, x, cb, arg) \
event_set(ev, x, EV_SIGNAL|EV_PERSIST, cb, arg)
#define signal_del(ev) event_del(ev)
#define signal_pending(ev, tv) event_pending(ev, EV_SIGNAL, tv)
#define signal_initialized(ev) ((ev)->ev_flags & EVLIST_INIT)
void event_set(struct event *, int, short, void (*)(int, short, void *), void *);
int event_add(struct event *, struct timeval *);
int event_del(struct event *);
void event_active(struct event *, int, short);
int event_pending(struct event *, short, struct timeval *);
#define event_initialized(ev) ((ev)->ev_flags & EVLIST_INIT)
#ifdef __cplusplus
}
#endif
#endif /* _EVENT_H_ */

40
lib/libevent/evsignal.h Normal file
View File

@ -0,0 +1,40 @@
/*
* 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Niels Provos.
* 4. 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.
*/
#ifndef _EVSIGNAL_H_
#define _EVSIGNAL_H_
void evsignal_init(sigset_t *);
void evsignal_process(void);
int evsignal_recalc(sigset_t *);
int evsignal_deliver(sigset_t *);
int evsignal_add(sigset_t *, struct event *);
int evsignal_del(sigset_t *, struct event *);
#endif /* _EVSIGNAL_H_ */

376
lib/libevent/kqueue.c Normal file
View File

@ -0,0 +1,376 @@
/* $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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Niels Provos.
* 4. 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
#include <sys/types.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#include <sys/_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 <err.h>
#ifdef HAVE_INTTYPES_H
#include <inttypes.h>
#endif
#ifdef USE_LOG
#include "log.h"
#else
#define LOG_DBG(x)
#define log_error warn
#endif
#ifdef HAVE_INTTYPES_H
#define INTPTR(x) (intptr_t)x
#else
#define INTPTR(x) x
#endif
#include "event.h"
extern struct event_list timequeue;
extern struct event_list eventqueue;
extern struct event_list addqueue;
#define EVLIST_X_KQINKERNEL 0x1000
#define NEVENT 64
struct kqop {
struct kevent *changes;
int nchanges;
struct kevent *events;
int nevents;
int kq;
} kqop;
void *kq_init (void);
int kq_add (void *, struct event *);
int kq_del (void *, struct event *);
int kq_recalc (void *, int);
int kq_dispatch (void *, struct timeval *);
struct eventop kqops = {
"kqueue",
kq_init,
kq_add,
kq_del,
kq_recalc,
kq_dispatch
};
void *
kq_init(void)
{
int kq;
/* Disable kqueue when this environment variable is set */
if (!issetugid() && getenv("EVENT_NOKQUEUE"))
return (NULL);
memset(&kqop, 0, sizeof(kqop));
/* Initalize the kernel queue */
if ((kq = kqueue()) == -1) {
log_error("kqueue");
return (NULL);
}
kqop.kq = kq;
/* Initalize fields */
kqop.changes = malloc(NEVENT * sizeof(struct kevent));
if (kqop.changes == NULL)
return (NULL);
kqop.events = malloc(NEVENT * sizeof(struct kevent));
if (kqop.events == NULL) {
free (kqop.changes);
return (NULL);
}
kqop.nevents = NEVENT;
return (&kqop);
}
int
kq_recalc(void *arg, int max)
{
return (0);
}
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) {
log_error("%s: malloc", __func__);
return (-1);
}
kqop->changes = newchange;
newresult = realloc(kqop->changes,
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) {
log_error("%s: malloc", __func__);
return (-1);
}
kqop->events = newchange;
kqop->nevents = nevents;
}
memcpy(&kqop->changes[kqop->nchanges++], kev, sizeof(struct kevent));
LOG_DBG((LOG_MISC, 70, "%s: fd %d %s%s",
__func__, 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 */
}
int
kq_dispatch(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;
int i, res;
TIMEVAL_TO_TIMESPEC(tv, &ts);
res = kevent(kqop->kq, changes, kqop->nchanges,
events, kqop->nevents, &ts);
kqop->nchanges = 0;
if (res == -1) {
if (errno != EINTR) {
log_error("kevent");
return (-1);
}
return (0);
}
LOG_DBG((LOG_MISC, 80, "%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.
* 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 == ENOENT)
continue;
return (-1);
}
ev = (struct event *)events[i].udata;
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 (!(ev->ev_events & EV_PERSIST)) {
ev->ev_flags &= ~EVLIST_X_KQINKERNEL;
event_del(ev);
}
event_active(ev, which,
ev->ev_events & EV_SIGNAL ? events[i].data : 1);
}
return (0);
}
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);
memset(&kev, 0, sizeof(kev));
kev.ident = nsignal;
kev.filter = EVFILT_SIGNAL;
kev.flags = EV_ADD;
if (!(ev->ev_events & EV_PERSIST))
kev.flags |= EV_ONESHOT;
kev.udata = INTPTR(ev);
if (kq_insert(kqop, &kev) == -1)
return (-1);
if (signal(nsignal, kq_sighandler) == SIG_ERR)
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_ADD;
if (!(ev->ev_events & EV_PERSIST))
kev.flags |= EV_ONESHOT;
kev.udata = INTPTR(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 = INTPTR(ev);
if (kq_insert(kqop, &kev) == -1)
return (-1);
ev->ev_flags |= EVLIST_X_KQINKERNEL;
}
return (0);
}
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);
memset(&kev, 0, sizeof(kev));
kev.ident = (int)signal;
kev.filter = EVFILT_SIGNAL;
kev.flags = EV_DELETE;
if (kq_insert(kqop, &kev) == -1)
return (-1);
if (signal(nsignal, SIG_DFL) == SIG_ERR)
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);
}

240
lib/libevent/poll.c Normal file
View File

@ -0,0 +1,240 @@
/* $OpenBSD: poll.c,v 1.2 2002/06/25 15:50:15 mickey Exp $ */
/*
* Copyright 2000-2003 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Niels Provos.
* 4. 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
#include <sys/types.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#include <sys/_time.h>
#endif
#include <sys/queue.h>
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <err.h>
#ifdef USE_LOG
#include "log.h"
#else
#define LOG_DBG(x)
#define log_error(x) perror(x)
#endif
#include "event.h"
#include "evsignal.h"
extern struct event_list eventqueue;
extern volatile sig_atomic_t evsignal_caught;
struct pollop {
int event_count; /* Highest number alloc */
struct pollfd *event_set;
struct event **event_back;
sigset_t evsigmask;
} pop;
void *poll_init (void);
int poll_add (void *, struct event *);
int poll_del (void *, struct event *);
int poll_recalc (void *, int);
int poll_dispatch (void *, struct timeval *);
struct eventop pollops = {
"poll",
poll_init,
poll_add,
poll_del,
poll_recalc,
poll_dispatch
};
void *
poll_init(void)
{
/* Disable kqueue when this environment variable is set */
if (!issetugid() && getenv("EVENT_NOPOLL"))
return (NULL);
memset(&pop, 0, sizeof(pop));
evsignal_init(&pop.evsigmask);
return (&pop);
}
/*
* Called with the highest fd that we know about. If it is 0, completely
* recalculate everything.
*/
int
poll_recalc(void *arg, int max)
{
struct pollop *pop = arg;
return (evsignal_recalc(&pop->evsigmask));
}
int
poll_dispatch(void *arg, struct timeval *tv)
{
int res, i, count, sec, nfds;
struct event *ev;
struct pollop *pop = arg;
count = pop->event_count;
nfds = 0;
TAILQ_FOREACH(ev, &eventqueue, ev_next) {
if (nfds + 1 >= count) {
if (count < 32)
count = 32;
else
count *= 2;
/* We need more file descriptors */
pop->event_set = realloc(pop->event_set,
count * sizeof(struct pollfd));
if (pop->event_set == NULL) {
log_error("realloc");
return (-1);
}
pop->event_back = realloc(pop->event_back,
count * sizeof(struct event *));
if (pop->event_back == NULL) {
log_error("realloc");
return (-1);
}
pop->event_count = count;
}
if (ev->ev_events & EV_WRITE) {
struct pollfd *pfd = &pop->event_set[nfds];
pfd->fd = ev->ev_fd;
pfd->events = POLLOUT;
pfd->revents = 0;
pop->event_back[nfds] = ev;
nfds++;
}
if (ev->ev_events & EV_READ) {
struct pollfd *pfd = &pop->event_set[nfds];
pfd->fd = ev->ev_fd;
pfd->events = POLLIN;
pfd->revents = 0;
pop->event_back[nfds] = ev;
nfds++;
}
}
if (evsignal_deliver(&pop->evsigmask) == -1)
return (-1);
sec = tv->tv_sec * 1000 + tv->tv_usec / 1000;
res = poll(pop->event_set, nfds, sec);
if (evsignal_recalc(&pop->evsigmask) == -1)
return (-1);
if (res == -1) {
if (errno != EINTR) {
log_error("poll");
return (-1);
}
evsignal_process();
return (0);
} else if (evsignal_caught)
evsignal_process();
LOG_DBG((LOG_MISC, 80, "%s: poll reports %d", __func__, res));
if (res == 0)
return (0);
for (i = 0; i < nfds; i++) {
res = 0;
if (pop->event_set[i].revents & POLLIN)
res = EV_READ;
else if (pop->event_set[i].revents & POLLOUT)
res = EV_WRITE;
if (res == 0)
continue;
ev = pop->event_back[i];
res &= ev->ev_events;
if (res) {
if (!(ev->ev_events & EV_PERSIST))
event_del(ev);
event_active(ev, res, 1);
}
}
return (0);
}
int
poll_add(void *arg, struct event *ev)
{
struct pollop *pop = arg;
if (ev->ev_events & EV_SIGNAL)
return (evsignal_add(&pop->evsigmask, ev));
return (0);
}
/*
* Nothing to be done here.
*/
int
poll_del(void *arg, struct event *ev)
{
struct pollop *pop = arg;
if (!(ev->ev_events & EV_SIGNAL))
return (0);
return (evsignal_del(&pop->evsigmask, ev));
}

View File

@ -0,0 +1,5 @@
# $NetBSD: shlib_version,v 1.1.1.1 2003/06/12 22:54:26 provos Exp $
# Remember to update distrib/sets/lists/base/shl.* when changing
#
major=0
minor=1

164
lib/libevent/signal.c Normal file
View File

@ -0,0 +1,164 @@
/* $OpenBSD: select.c,v 1.2 2002/06/25 15:50:15 mickey 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by Niels Provos.
* 4. 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.
*/
#include "config.h"
#include <sys/types.h>
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#else
#include <sys/_time.h>
#endif
#include <sys/queue.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <err.h>
#ifdef USE_LOG
#include "log.h"
#else
#define LOG_DBG(x)
#define log_error(x) perror(x)
#endif
#include "event.h"
extern struct event_list signalqueue;
static short evsigcaught[NSIG];
static int needrecalc;
volatile sig_atomic_t evsignal_caught = 0;
void evsignal_process(void);
int evsignal_recalc(sigset_t *);
int evsignal_deliver(sigset_t *);
void
evsignal_init(sigset_t *evsigmask)
{
sigemptyset(evsigmask);
}
int
evsignal_add(sigset_t *evsigmask, struct event *ev)
{
int signal;
if (ev->ev_events & (EV_READ|EV_WRITE))
errx(1, "%s: EV_SIGNAL incompatible use", __func__);
signal = EVENT_SIGNAL(ev);
sigaddset(evsigmask, signal);
return (0);
}
/*
* Nothing to be done here.
*/
int
evsignal_del(sigset_t *evsigmask, struct event *ev)
{
int signal;
signal = EVENT_SIGNAL(ev);
sigdelset(evsigmask, signal);
needrecalc = 1;
return (sigaction(EVENT_SIGNAL(ev),(struct sigaction *)SIG_DFL, NULL));
}
static void
evsignal_handler(int sig)
{
evsigcaught[sig]++;
evsignal_caught = 1;
}
int
evsignal_recalc(sigset_t *evsigmask)
{
struct sigaction sa;
struct event *ev;
if (TAILQ_FIRST(&signalqueue) == NULL && !needrecalc)
return (0);
needrecalc = 0;
if (sigprocmask(SIG_BLOCK, evsigmask, NULL) == -1)
return (-1);
/* Reinstall our signal handler. */
memset(&sa, 0, sizeof(sa));
sa.sa_handler = evsignal_handler;
sa.sa_mask = *evsigmask;
sa.sa_flags |= SA_RESTART;
TAILQ_FOREACH(ev, &signalqueue, ev_signal_next) {
if (sigaction(EVENT_SIGNAL(ev), &sa, NULL) == -1)
return (-1);
}
return (0);
}
int
evsignal_deliver(sigset_t *evsigmask)
{
if (TAILQ_FIRST(&signalqueue) == NULL)
return (0);
return (sigprocmask(SIG_UNBLOCK, evsigmask, NULL));
/* XXX - pending signals handled here */
}
void
evsignal_process(void)
{
struct event *ev;
short ncalls;
TAILQ_FOREACH(ev, &signalqueue, ev_signal_next) {
ncalls = evsigcaught[EVENT_SIGNAL(ev)];
if (ncalls) {
if (!(ev->ev_events & EV_PERSIST))
event_del(ev);
event_active(ev, EV_SIGNAL, ncalls);
}
}
memset(evsigcaught, 0, sizeof(evsigcaught));
evsignal_caught = 0;
}