mirror of
https://github.com/darlinghq/darling-libkqueue.git
synced 2025-03-01 08:47:24 +00:00
initial import
git-svn-id: svn://svn.code.sf.net/p/libkqueue/code/trunk@1 fb4e3144-bc1c-4b72-a658-5bcd248dd7f7
This commit is contained in:
commit
51a674414b
57
Makefile
Normal file
57
Makefile
Normal file
@ -0,0 +1,57 @@
|
||||
#
|
||||
# Copyright (c) 2009 Mark Heily <mark@heily.com>
|
||||
#
|
||||
# Permission to use, copy, modify, and distribute this software for any
|
||||
# purpose with or without fee is hereby granted, provided that the above
|
||||
# copyright notice and this permission notice appear in all copies.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
#
|
||||
PROGRAM=libkqueue
|
||||
INSTALL=/usr/bin/install
|
||||
PREFIX=/usr
|
||||
DISTFILES=*.c *.h kqueue.2 README Makefile configure os sys
|
||||
SOURCES=src/$(UNAME)/*.c
|
||||
CFLAGS=-fPIC -I. -Wall -Werror -fvisibility=hidden
|
||||
FILTERS=vnode.c timer.c signal.c socket.c user.c
|
||||
|
||||
include config.mk
|
||||
|
||||
build:
|
||||
gcc $(CFLAGS) -c *.c
|
||||
ar rcs libkqueue.a *.o
|
||||
gcc -shared -Wl,-soname,libkqueue.so -o libkqueue.so *.o
|
||||
|
||||
install:
|
||||
mkdir -p $(PREFIX)/include/kqueue/sys
|
||||
cp event.h $(PREFIX)/include/kqueue/sys
|
||||
cp libkqueue.so $(PREFIX)/lib
|
||||
cp kqueue.2 $(PREFIX)/share/man/man2/kqueue.2
|
||||
ln -s kqueue.2 $(PREFIX)/share/man/man2/kevent.2
|
||||
|
||||
check:
|
||||
make build CFLAGS="$(CFLAGS) -g -O0 -DKQUEUE_DEBUG -DUNIT_TEST"
|
||||
gcc -g -O0 $(CFLAGS) test.c libkqueue.a -lpthread -lrt
|
||||
./a.out
|
||||
|
||||
check-installed:
|
||||
gcc -g -O0 -Wall -Werror -I$(PREFIX)/kqueue test.c -lkqueue -lpthread -lrt
|
||||
./a.out
|
||||
|
||||
dist:
|
||||
mkdir $(PROGRAM)
|
||||
cp -R $(DISTFILES) $(PROGRAM)
|
||||
tar zcvf $(PROGRAM)-`date +%y%m%d_%H%M`.tar.gz $(PROGRAM)
|
||||
rm -rf $(PROGRAM)
|
||||
|
||||
clean:
|
||||
rm -f a.out *.a *.o *.so $(FILTERS)
|
||||
|
||||
distclean: clean
|
||||
rm -f *.tar.gz config.mk
|
51
README
Normal file
51
README
Normal file
@ -0,0 +1,51 @@
|
||||
libkqueue emulates the kqueue(2) kernel event notification mechanism
|
||||
on platforms which do not provide it natively. Currently, the only
|
||||
supported platform is Linux and the only tested platform is
|
||||
Ubuntu x64 with kernel 2.6.28.
|
||||
|
||||
|
||||
kevent.flags Implementation Status
|
||||
|
||||
ADD DELETE ENABLE DISABLE DISPATCH ONESHOT CLEAR EOF
|
||||
|
||||
READ/WRITE Y Y Y Y N Y N N
|
||||
AIO N N N N N N N N
|
||||
VNODE Y N N N N N N N
|
||||
PROC NO -- Cannot fully implement on Linux
|
||||
SIGNAL Y N N N N N N N
|
||||
NETDEV NO -- Don't know how to implement on Linux
|
||||
TIMER Y N N N N N N N
|
||||
USER Totally broken
|
||||
|
||||
kevent.fflags Implementation Status
|
||||
|
||||
Status
|
||||
READ
|
||||
NOTE_LOWAT No
|
||||
EV_EOF No
|
||||
WRITE
|
||||
NOTE_LOWAT No
|
||||
EV_EOF No
|
||||
VNODE
|
||||
NOTE_DELETE No
|
||||
NOTE_WRITE No
|
||||
NOTE_EXTEND No
|
||||
NOTE_ATTRIB No
|
||||
NOTE_LINK No
|
||||
NOTE_RENAME No
|
||||
NOTE_REVOKE N/A
|
||||
USER
|
||||
NOTE_FFNOP Ignore the input fflags.
|
||||
NOTE_FFAND Bitwise AND fflags.
|
||||
NOTE_FFOR Bitwise OR fflags.
|
||||
NOTE_COPY Copy fflags.
|
||||
NOTE_FFCTRLMASK Control mask for fflags.
|
||||
NOTE_FFLAGSMASK User defined flag mask for fflags.
|
||||
NOTE_TRIGGER Cause the event to be triggered.
|
||||
|
||||
Filter sets kevent.data structure when returning
|
||||
EV_RECEIPT Yes
|
||||
kevent.data No
|
||||
|
||||
--
|
||||
Mark Heily <mark@heily.com>
|
5
config.h
Normal file
5
config.h
Normal file
@ -0,0 +1,5 @@
|
||||
# AUTOMATICALLY GENERATED -- DO NOT EDIT
|
||||
#define HAVE_SYS_EPOLL_H
|
||||
#define HAVE_SYS_INOTIFY_H
|
||||
#undef HAVE_SYS_SIGNALFD_H
|
||||
#undef HAVE_SYS_TIMERFD_H
|
34
configure
vendored
Executable file
34
configure
vendored
Executable file
@ -0,0 +1,34 @@
|
||||
#!/bin/sh
|
||||
|
||||
check_header() {
|
||||
sym=`echo "HAVE_$1" | tr a-z A-Z | sed 's,[./],_,g'`
|
||||
if [ -f /usr/include/$1 ] ; then
|
||||
echo "#define $sym" >> config.h
|
||||
return 0
|
||||
else
|
||||
echo "#undef $sym" >> config.h
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
echo "Checking operating system type... `uname`"
|
||||
kernel=`uname -s | tr A-Z a-z`
|
||||
target=$kernel
|
||||
|
||||
echo "Creating config.h"
|
||||
echo "# AUTOMATICALLY GENERATED -- DO NOT EDIT" > config.h
|
||||
if [ $kernel = "linux" ] ; then
|
||||
check_header sys/epoll.h && ln -s os/linux/socket.c \
|
||||
|| ln -s os/posix/socket.c
|
||||
check_header sys/inotify.h && ln -s os/linux/vnode.c \
|
||||
|| ln -s os/posix/vnode.c
|
||||
check_header sys/signalfd.h && ln -s os/linux/signal.c \
|
||||
|| ln -s os/posix/signal.c
|
||||
check_header sys/timerfd.h && ln -s os/linux/timer.c \
|
||||
|| ln -s os/posix/timer.c
|
||||
ln -s os/posix/user.c
|
||||
fi
|
||||
|
||||
echo "Creating config.mk"
|
||||
echo "# AUTOMATICALLY GENERATED -- DO NOT EDIT" > config.mk
|
||||
echo "UNAME=$target" >> config.mk
|
172
filter.c
Normal file
172
filter.c
Normal file
@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Mark Heily <mark@heily.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "private.h"
|
||||
|
||||
extern const struct filter evfilt_read;
|
||||
extern const struct filter evfilt_write;
|
||||
extern const struct filter evfilt_signal;
|
||||
extern const struct filter evfilt_vnode;
|
||||
extern const struct filter evfilt_timer;
|
||||
extern const struct filter evfilt_user;
|
||||
|
||||
static int
|
||||
filter_register(struct kqueue *kq, short filter, const struct filter *src)
|
||||
{
|
||||
struct filter *dst;
|
||||
u_int filt;
|
||||
int rv = 0;
|
||||
|
||||
filt = (-1 * filter) - 1;
|
||||
if (filt >= EVFILT_SYSCOUNT)
|
||||
return (-1);
|
||||
|
||||
dst = &kq->kq_filt[filt];
|
||||
memcpy(dst, src, sizeof(*src));
|
||||
dst->kf_kqueue = kq;
|
||||
KNOTELIST_INIT(&dst->knl);
|
||||
if (src->kf_init == NULL) {
|
||||
dbg_puts("filter has no initializer");
|
||||
return (-1);
|
||||
}
|
||||
|
||||
rv = src->kf_init(dst);
|
||||
if (rv < 0) {
|
||||
dbg_puts("filter failed to initialize");
|
||||
dst->kf_id = 0; /* FIXME: lame magic constant */
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* Add the filter's event descriptor to the main fdset */
|
||||
if (dst->kf_pfd <= 0) {
|
||||
dbg_printf("FIXME - filter %s did not return a fd to poll!",
|
||||
filter_name(filter));
|
||||
return (-1);
|
||||
}
|
||||
FD_SET(dst->kf_pfd, &kq->kq_fds);
|
||||
if (dst->kf_pfd > kq->kq_nfds)
|
||||
kq->kq_nfds = dst->kf_pfd;
|
||||
dbg_printf("fds: added %d (nfds=%d)", dst->kf_pfd, kq->kq_nfds);
|
||||
dbg_printf("%s registered", filter_name(filter));
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
filter_register_all(struct kqueue *kq)
|
||||
{
|
||||
int rv;
|
||||
|
||||
FD_ZERO(&kq->kq_fds);
|
||||
rv = 0;
|
||||
rv += filter_register(kq, EVFILT_READ, &evfilt_read);
|
||||
rv += filter_register(kq, EVFILT_WRITE, &evfilt_write);
|
||||
rv += filter_register(kq, EVFILT_SIGNAL, &evfilt_signal);
|
||||
rv += filter_register(kq, EVFILT_VNODE, &evfilt_vnode);
|
||||
rv += filter_register(kq, EVFILT_TIMER, &evfilt_timer);
|
||||
rv += filter_register(kq, EVFILT_USER, &evfilt_user);
|
||||
kq->kq_nfds++;
|
||||
if (rv != 0) {
|
||||
filter_unregister_all(kq);
|
||||
return (-1);
|
||||
} else {
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
filter_unregister_all(struct kqueue *kq)
|
||||
{
|
||||
struct knote *n1, *n2;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < EVFILT_SYSCOUNT; i++) {
|
||||
if (kq->kq_filt[i].kf_id == 0)
|
||||
continue;
|
||||
|
||||
if (kq->kq_filt[i].kf_destroy != NULL)
|
||||
kq->kq_filt[i].kf_destroy(&kq->kq_filt[i]);
|
||||
|
||||
/* Destroy all knotes associated with this filter */
|
||||
for (n1 = LIST_FIRST(&kq->kq_filt[i].knl); n1 != NULL; n1 = n2) {
|
||||
n2 = LIST_NEXT(n1, entries);
|
||||
free(n1);
|
||||
}
|
||||
}
|
||||
memset(&kq->kq_filt[0], 0, sizeof(kq->kq_filt));
|
||||
}
|
||||
|
||||
int
|
||||
filter_socketpair(struct filter *filt)
|
||||
{
|
||||
int sockfd[2];
|
||||
|
||||
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd) < 0)
|
||||
return (-1);
|
||||
|
||||
fcntl(sockfd[0], F_SETFL, O_NONBLOCK);
|
||||
filt->kf_wfd = sockfd[0];
|
||||
filt->kf_pfd = sockfd[1];
|
||||
return (0);
|
||||
}
|
||||
|
||||
struct filter *
|
||||
filter_lookup(struct kqueue *kq, short id)
|
||||
{
|
||||
id = (-1 * id) - 1;
|
||||
if (id < 0 || id >= EVFILT_SYSCOUNT) {
|
||||
errno = EINVAL;
|
||||
return (NULL);
|
||||
}
|
||||
if (kq->kq_filt[id].kf_copyin == NULL) {
|
||||
errno = ENOTSUP;
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
return (&kq->kq_filt[id]);
|
||||
}
|
||||
|
||||
const char *
|
||||
filter_name(short filt)
|
||||
{
|
||||
const char *fname[EVFILT_SYSCOUNT] = {
|
||||
"EVFILT_READ",
|
||||
"EVFILT_WRITE",
|
||||
"EVFILT_AIO",
|
||||
"EVFILT_VNODE",
|
||||
"EVFILT_PROC",
|
||||
"EVFILT_SIGNAL",
|
||||
"EVFILT_TIMER",
|
||||
"EVFILT_NETDEV",
|
||||
"EVFILT_FS",
|
||||
"EVFILT_LIO",
|
||||
"EVFILT_USER"
|
||||
};
|
||||
|
||||
if (~filt >= EVFILT_SYSCOUNT)
|
||||
return "EVFILT_BAD_RANGE";
|
||||
else
|
||||
return fname[~filt];
|
||||
}
|
230
kevent.c
Normal file
230
kevent.c
Normal file
@ -0,0 +1,230 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Mark Heily <mark@heily.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
/* To get asprintf(3) */
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sys/event.h"
|
||||
#include "private.h"
|
||||
|
||||
const char *
|
||||
kevent_dump(struct kevent *kev)
|
||||
{
|
||||
char buf[512];
|
||||
snprintf(&buf[0], sizeof(buf), "[filter=%d,flags=%d,ident=%u,udata=%p]",
|
||||
kev->filter,
|
||||
kev->flags,
|
||||
(u_int) kev->ident,
|
||||
kev->udata);
|
||||
return (strdup(buf));
|
||||
}
|
||||
|
||||
static void
|
||||
kevent_error(struct kevent *dst, const struct kevent *src, int data)
|
||||
{
|
||||
memcpy(dst, src, sizeof(*src));
|
||||
dst->data = data;
|
||||
}
|
||||
|
||||
static int
|
||||
kevent_copyin(struct kqueue *kq, const struct kevent *src, int nchanges,
|
||||
struct kevent *eventlist, int nevents)
|
||||
{
|
||||
struct knote *dst;
|
||||
struct filter *filt;
|
||||
int kn_alloc,status;
|
||||
|
||||
for (; nchanges > 0; src++, nchanges--) {
|
||||
|
||||
if ((filt = filter_lookup(kq, src->filter)) == NULL) {
|
||||
status = -EINVAL;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Retrieve an existing knote object, or create a new one.
|
||||
*/
|
||||
kn_alloc = 0;
|
||||
dst = knote_lookup(filt, src->ident);
|
||||
if (dst == NULL) {
|
||||
if (src->flags & EV_ADD) {
|
||||
if ((dst = knote_new(filt)) == NULL) {
|
||||
status = -ENOMEM;
|
||||
goto err_out;
|
||||
}
|
||||
dbg_puts(kevent_dump(&dst->kev));
|
||||
kn_alloc = 1;
|
||||
} else if (src->flags & EV_ENABLE
|
||||
|| src->flags & EV_DISABLE
|
||||
|| src->flags & EV_DELETE) {
|
||||
status = -ENOENT;
|
||||
goto err_out;
|
||||
} else {
|
||||
/* flags == 0 or no action */
|
||||
status = -EINVAL;
|
||||
goto err_out;
|
||||
}
|
||||
}
|
||||
|
||||
if (filt->kf_copyin(filt, dst, src) < 0) {
|
||||
if (kn_alloc)
|
||||
knote_free(dst);
|
||||
status = -EBADMSG;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the knote flags based on src->flags.
|
||||
*/
|
||||
if (src->flags & EV_ENABLE) {
|
||||
dst->kev.flags |= EV_ENABLE;
|
||||
dst->kev.flags &= ~EV_DISABLE;
|
||||
}
|
||||
if (src->flags & EV_DISABLE) {
|
||||
dst->kev.flags &= ~EV_ENABLE;
|
||||
dst->kev.flags |= EV_DISABLE;
|
||||
}
|
||||
if (src->flags & EV_DELETE) {
|
||||
knote_free(dst);
|
||||
}
|
||||
if (src->flags & EV_RECEIPT) {
|
||||
status = 0;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
continue;
|
||||
|
||||
err_out:
|
||||
if (status != 0 && kn_alloc)
|
||||
knote_free(dst);
|
||||
if (nevents > 0) {
|
||||
kevent_error(eventlist++, src, status);
|
||||
nevents--;
|
||||
} else {
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
kevent(int kqfd, const struct kevent *changelist, int nchanges,
|
||||
struct kevent *eventlist, int nevents,
|
||||
const struct timespec *timeout)
|
||||
{
|
||||
struct kqueue *kq;
|
||||
struct filter *filt;
|
||||
fd_set fds;
|
||||
int i, rv, n, nret;
|
||||
|
||||
errno = 0;
|
||||
|
||||
kq = kqueue_lookup(kqfd);
|
||||
if (kq == NULL) {
|
||||
dbg_printf("fd lookup failed; fd=%d", kqfd);
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Process each kevent on the changelist.
|
||||
*/
|
||||
if (nchanges) {
|
||||
kqueue_lock(kq);
|
||||
rv = kevent_copyin(kq, changelist, nchanges, eventlist, nevents);
|
||||
kqueue_unlock(kq);
|
||||
if (rv < 0)
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* Determine if we need to wait for events. */
|
||||
if (nevents == 0)
|
||||
return (0);
|
||||
if (nevents > MAX_KEVENT)
|
||||
nevents = MAX_KEVENT;
|
||||
|
||||
/*
|
||||
* Wait for one or more filters to have events.
|
||||
*/
|
||||
fds = kq->kq_fds;
|
||||
#if BROKEN
|
||||
n = pselect(kq->kq_nfds, &fds, NULL , NULL, timeout, NULL);
|
||||
#else
|
||||
/* BROKEn, but testing vnode crap */
|
||||
struct timeval tv;
|
||||
struct timeval *tvp = &tv;
|
||||
if (timeout) {
|
||||
tv.tv_sec = timeout->tv_sec;
|
||||
tv.tv_usec = timeout->tv_nsec / 1000;
|
||||
} else {
|
||||
dbg_puts("null timeout..");
|
||||
tvp = NULL;
|
||||
}
|
||||
n = select(kq->kq_nfds, &fds, NULL, NULL, tvp);
|
||||
#endif
|
||||
if (n < 0) {
|
||||
if (errno == EINTR) {
|
||||
dbg_puts("signal caught");
|
||||
return (-1);
|
||||
}
|
||||
dbg_perror("pselect(2)");
|
||||
return (-1);
|
||||
}
|
||||
dbg_printf("pselect(2): %d bits set", n);
|
||||
if (n == 0)
|
||||
return (0);
|
||||
|
||||
/*
|
||||
* Process each event and place it on the eventlist
|
||||
*/
|
||||
nret = 0;
|
||||
kqueue_lock(kq);
|
||||
for (i = 0; (i < EVFILT_SYSCOUNT && n > 0 && nevents > 0); i++) {
|
||||
dbg_printf("eventlist: n = %d nevents = %d", n, nevents);
|
||||
filt = &kq->kq_filt[i];
|
||||
dbg_printf("pfd[%d] = %d", i, filt->kf_pfd);
|
||||
if (FD_ISSET(filt->kf_pfd, &fds)) {
|
||||
dbg_printf("event(s) for filter #%d", i);
|
||||
rv = filt->kf_copyout(filt, eventlist, nevents);
|
||||
if (rv < 0) {
|
||||
kqueue_unlock(kq);
|
||||
dbg_puts("kevent_copyout failed");
|
||||
return (-1);
|
||||
}
|
||||
nret += rv;
|
||||
eventlist += rv;
|
||||
nevents -= rv;
|
||||
n--;
|
||||
}
|
||||
}
|
||||
kqueue_unlock(kq);
|
||||
|
||||
return (nret);
|
||||
}
|
67
knote.c
Normal file
67
knote.c
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Mark Heily <mark@heily.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/queue.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "private.h"
|
||||
|
||||
/* TODO: These must be called with the kqueue lock held */
|
||||
|
||||
struct knote *
|
||||
knote_new(struct filter *filt)
|
||||
{
|
||||
struct knote *dst;
|
||||
|
||||
if ((dst = calloc(1, sizeof(*dst))) == NULL)
|
||||
return (NULL);
|
||||
KNOTE_INSERT(&filt->knl, dst);
|
||||
return (dst);
|
||||
}
|
||||
|
||||
void
|
||||
knote_free(struct knote *kn)
|
||||
{
|
||||
dbg_printf("knote_free(): filt=%d, ident=%u",
|
||||
kn->kev.filter, (u_int) kn->kev.ident);
|
||||
LIST_REMOVE(kn, entries);
|
||||
free(kn);
|
||||
}
|
||||
|
||||
/* TODO: rename to knote_lookup_ident */
|
||||
struct knote *
|
||||
knote_lookup(struct filter *filt, short ident)
|
||||
{
|
||||
struct knote *kn;
|
||||
LIST_FOREACH(kn, &filt->knl, entries) {
|
||||
if (ident == kn->kev.ident)
|
||||
break;
|
||||
}
|
||||
return (kn);
|
||||
}
|
||||
|
||||
struct knote *
|
||||
knote_lookup_data(struct filter *filt, intptr_t data)
|
||||
{
|
||||
struct knote *kn;
|
||||
LIST_FOREACH(kn, &filt->knl, entries) {
|
||||
if (data == kn->kev.data)
|
||||
break;
|
||||
}
|
||||
return (kn);
|
||||
}
|
608
kqueue.2
Normal file
608
kqueue.2
Normal file
@ -0,0 +1,608 @@
|
||||
.\" $FreeBSD: Revision: 197243$
|
||||
.\" Copyright (c) 2000 Jonathan Lemon
|
||||
.\" 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.
|
||||
.\"
|
||||
.\" 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 OR CONTRIBUTORS 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.
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd September 15, 2009
|
||||
.Dt KQUEUE 2
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm kqueue ,
|
||||
.Nm kevent
|
||||
.Nd kernel event notification mechanism
|
||||
.Sh LIBRARY
|
||||
.Lb libc
|
||||
.Sh SYNOPSIS
|
||||
.In sys/types.h
|
||||
.In sys/event.h
|
||||
.In sys/time.h
|
||||
.Ft int
|
||||
.Fn kqueue "void"
|
||||
.Ft int
|
||||
.Fn kevent "int kq" "const struct kevent *changelist" "int nchanges" "struct kevent *eventlist" "int nevents" "const struct timespec *timeout"
|
||||
.Fn EV_SET "&kev" ident filter flags fflags data udata
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Fn kqueue
|
||||
system call
|
||||
provides a generic method of notifying the user when an event
|
||||
happens or a condition holds, based on the results of small
|
||||
pieces of kernel code termed filters.
|
||||
A kevent is identified by the (ident, filter) pair; there may only
|
||||
be one unique kevent per kqueue.
|
||||
.Pp
|
||||
The filter is executed upon the initial registration of a kevent
|
||||
in order to detect whether a preexisting condition is present, and is also
|
||||
executed whenever an event is passed to the filter for evaluation.
|
||||
If the filter determines that the condition should be reported,
|
||||
then the kevent is placed on the kqueue for the user to retrieve.
|
||||
.Pp
|
||||
The filter is also run when the user attempts to retrieve the kevent
|
||||
from the kqueue.
|
||||
If the filter indicates that the condition that triggered
|
||||
the event no longer holds, the kevent is removed from the kqueue and
|
||||
is not returned.
|
||||
.Pp
|
||||
Multiple events which trigger the filter do not result in multiple
|
||||
kevents being placed on the kqueue; instead, the filter will aggregate
|
||||
the events into a single struct kevent.
|
||||
Calling
|
||||
.Fn close
|
||||
on a file descriptor will remove any kevents that reference the descriptor.
|
||||
.Pp
|
||||
The
|
||||
.Fn kqueue
|
||||
system call
|
||||
creates a new kernel event queue and returns a descriptor.
|
||||
The queue is not inherited by a child created with
|
||||
.Xr fork 2 .
|
||||
However, if
|
||||
.Xr rfork 2
|
||||
is called without the
|
||||
.Dv RFFDG
|
||||
flag, then the descriptor table is shared,
|
||||
which will allow sharing of the kqueue between two processes.
|
||||
.Pp
|
||||
The
|
||||
.Fn kevent
|
||||
system call
|
||||
is used to register events with the queue, and return any pending
|
||||
events to the user.
|
||||
The
|
||||
.Fa changelist
|
||||
argument
|
||||
is a pointer to an array of
|
||||
.Va kevent
|
||||
structures, as defined in
|
||||
.In sys/event.h .
|
||||
All changes contained in the
|
||||
.Fa changelist
|
||||
are applied before any pending events are read from the queue.
|
||||
The
|
||||
.Fa nchanges
|
||||
argument
|
||||
gives the size of
|
||||
.Fa changelist .
|
||||
The
|
||||
.Fa eventlist
|
||||
argument
|
||||
is a pointer to an array of kevent structures.
|
||||
The
|
||||
.Fa nevents
|
||||
argument
|
||||
determines the size of
|
||||
.Fa eventlist .
|
||||
When
|
||||
.Fa nevents
|
||||
is zero,
|
||||
.Fn kevent
|
||||
will return immediately even if there is a
|
||||
.Fa timeout
|
||||
specified unlike
|
||||
.Xr select 2 .
|
||||
If
|
||||
.Fa timeout
|
||||
is a non-NULL pointer, it specifies a maximum interval to wait
|
||||
for an event, which will be interpreted as a struct timespec.
|
||||
If
|
||||
.Fa timeout
|
||||
is a NULL pointer,
|
||||
.Fn kevent
|
||||
waits indefinitely.
|
||||
To effect a poll, the
|
||||
.Fa timeout
|
||||
argument should be non-NULL, pointing to a zero-valued
|
||||
.Va timespec
|
||||
structure.
|
||||
The same array may be used for the
|
||||
.Fa changelist
|
||||
and
|
||||
.Fa eventlist .
|
||||
.Pp
|
||||
The
|
||||
.Fn EV_SET
|
||||
macro is provided for ease of initializing a
|
||||
kevent structure.
|
||||
.Pp
|
||||
The
|
||||
.Va kevent
|
||||
structure is defined as:
|
||||
.Bd -literal
|
||||
struct kevent {
|
||||
uintptr_t ident; /* identifier for this event */
|
||||
short filter; /* filter for event */
|
||||
u_short flags; /* action flags for kqueue */
|
||||
u_int fflags; /* filter flag value */
|
||||
intptr_t data; /* filter data value */
|
||||
void *udata; /* opaque user data identifier */
|
||||
};
|
||||
.Ed
|
||||
.Pp
|
||||
The fields of
|
||||
.Fa struct kevent
|
||||
are:
|
||||
.Bl -tag -width XXXfilter
|
||||
.It ident
|
||||
Value used to identify this event.
|
||||
The exact interpretation is determined by the attached filter,
|
||||
but often is a file descriptor.
|
||||
.It filter
|
||||
Identifies the kernel filter used to process this event.
|
||||
The pre-defined
|
||||
system filters are described below.
|
||||
.It flags
|
||||
Actions to perform on the event.
|
||||
.It fflags
|
||||
Filter-specific flags.
|
||||
.It data
|
||||
Filter-specific data value.
|
||||
.It udata
|
||||
Opaque user-defined value passed through the kernel unchanged.
|
||||
.El
|
||||
.Pp
|
||||
The
|
||||
.Va flags
|
||||
field can contain the following values:
|
||||
.Bl -tag -width XXXEV_ONESHOT
|
||||
.It EV_ADD
|
||||
Adds the event to the kqueue.
|
||||
Re-adding an existing event
|
||||
will modify the parameters of the original event, and not result
|
||||
in a duplicate entry.
|
||||
Adding an event automatically enables it,
|
||||
unless overridden by the EV_DISABLE flag.
|
||||
.It EV_ENABLE
|
||||
Permit
|
||||
.Fn kevent
|
||||
to return the event if it is triggered.
|
||||
.It EV_DISABLE
|
||||
Disable the event so
|
||||
.Fn kevent
|
||||
will not return it.
|
||||
The filter itself is not disabled.
|
||||
.It EV_DISPATCH
|
||||
Disable the event source immediately after delivery of an event.
|
||||
See
|
||||
.Dv EV_DISABLE
|
||||
above.
|
||||
.It EV_DELETE
|
||||
Removes the event from the kqueue.
|
||||
Events which are attached to
|
||||
file descriptors are automatically deleted on the last close of
|
||||
the descriptor.
|
||||
.It EV_RECEIPT
|
||||
This flag is useful for making bulk changes to a kqueue without draining
|
||||
any pending events.
|
||||
When passed as input, it forces
|
||||
.Dv EV_ERROR
|
||||
to always be returned.
|
||||
When a filter is successfully added the
|
||||
.Va data
|
||||
field will be zero.
|
||||
.It EV_ONESHOT
|
||||
Causes the event to return only the first occurrence of the filter
|
||||
being triggered.
|
||||
After the user retrieves the event from the kqueue,
|
||||
it is deleted.
|
||||
.It EV_CLEAR
|
||||
After the event is retrieved by the user, its state is reset.
|
||||
This is useful for filters which report state transitions
|
||||
instead of the current state.
|
||||
Note that some filters may automatically
|
||||
set this flag internally.
|
||||
.It EV_EOF
|
||||
Filters may set this flag to indicate filter-specific EOF condition.
|
||||
.It EV_ERROR
|
||||
See
|
||||
.Sx RETURN VALUES
|
||||
below.
|
||||
.El
|
||||
.Pp
|
||||
The predefined system filters are listed below.
|
||||
Arguments may be passed to and from the filter via the
|
||||
.Va fflags
|
||||
and
|
||||
.Va data
|
||||
fields in the kevent structure.
|
||||
.Bl -tag -width EVFILT_SIGNAL
|
||||
.It EVFILT_READ
|
||||
Takes a descriptor as the identifier, and returns whenever
|
||||
there is data available to read.
|
||||
The behavior of the filter is slightly different depending
|
||||
on the descriptor type.
|
||||
.Pp
|
||||
.Bl -tag -width 2n
|
||||
.It Sockets
|
||||
Sockets which have previously been passed to
|
||||
.Fn listen
|
||||
return when there is an incoming connection pending.
|
||||
.Va data
|
||||
contains the size of the listen backlog.
|
||||
.Pp
|
||||
Other socket descriptors return when there is data to be read,
|
||||
subject to the
|
||||
.Dv SO_RCVLOWAT
|
||||
value of the socket buffer.
|
||||
This may be overridden with a per-filter low water mark at the
|
||||
time the filter is added by setting the
|
||||
NOTE_LOWAT
|
||||
flag in
|
||||
.Va fflags ,
|
||||
and specifying the new low water mark in
|
||||
.Va data .
|
||||
On return,
|
||||
.Va data
|
||||
contains the number of bytes of protocol data available to read.
|
||||
.Pp
|
||||
If the read direction of the socket has shutdown, then the filter
|
||||
also sets EV_EOF in
|
||||
.Va flags ,
|
||||
and returns the socket error (if any) in
|
||||
.Va fflags .
|
||||
It is possible for EOF to be returned (indicating the connection is gone)
|
||||
while there is still data pending in the socket buffer.
|
||||
.It Vnodes
|
||||
Returns when the file pointer is not at the end of file.
|
||||
.Va data
|
||||
contains the offset from current position to end of file,
|
||||
and may be negative.
|
||||
.It "Fifos, Pipes"
|
||||
Returns when the there is data to read;
|
||||
.Va data
|
||||
contains the number of bytes available.
|
||||
.Pp
|
||||
When the last writer disconnects, the filter will set EV_EOF in
|
||||
.Va flags .
|
||||
This may be cleared by passing in EV_CLEAR, at which point the
|
||||
filter will resume waiting for data to become available before
|
||||
returning.
|
||||
.It "BPF devices"
|
||||
Returns when the BPF buffer is full, the BPF timeout has expired, or
|
||||
when the BPF has
|
||||
.Dq immediate mode
|
||||
enabled and there is any data to read;
|
||||
.Va data
|
||||
contains the number of bytes available.
|
||||
.El
|
||||
.It EVFILT_WRITE
|
||||
Takes a descriptor as the identifier, and returns whenever
|
||||
it is possible to write to the descriptor.
|
||||
For sockets, pipes
|
||||
and fifos,
|
||||
.Va data
|
||||
will contain the amount of space remaining in the write buffer.
|
||||
The filter will set EV_EOF when the reader disconnects, and for
|
||||
the fifo case, this may be cleared by use of EV_CLEAR.
|
||||
Note that this filter is not supported for vnodes or BPF devices.
|
||||
.Pp
|
||||
For sockets, the low water mark and socket error handling is
|
||||
identical to the EVFILT_READ case.
|
||||
.It EVFILT_AIO
|
||||
The sigevent portion of the AIO request is filled in, with
|
||||
.Va sigev_notify_kqueue
|
||||
containing the descriptor of the kqueue that the event should
|
||||
be attached to,
|
||||
.Va sigev_value
|
||||
containing the udata value, and
|
||||
.Va sigev_notify
|
||||
set to SIGEV_KEVENT.
|
||||
When the
|
||||
.Fn aio_*
|
||||
system call is made, the event will be registered
|
||||
with the specified kqueue, and the
|
||||
.Va ident
|
||||
argument set to the
|
||||
.Fa struct aiocb
|
||||
returned by the
|
||||
.Fn aio_*
|
||||
system call.
|
||||
The filter returns under the same conditions as aio_error.
|
||||
.It EVFILT_VNODE
|
||||
Takes a file descriptor as the identifier and the events to watch for in
|
||||
.Va fflags ,
|
||||
and returns when one or more of the requested events occurs on the descriptor.
|
||||
The events to monitor are:
|
||||
.Bl -tag -width XXNOTE_RENAME
|
||||
.It NOTE_DELETE
|
||||
The
|
||||
.Fn unlink
|
||||
system call
|
||||
was called on the file referenced by the descriptor.
|
||||
.It NOTE_WRITE
|
||||
A write occurred on the file referenced by the descriptor.
|
||||
.It NOTE_EXTEND
|
||||
The file referenced by the descriptor was extended.
|
||||
.It NOTE_ATTRIB
|
||||
The file referenced by the descriptor had its attributes changed.
|
||||
.It NOTE_LINK
|
||||
The link count on the file changed.
|
||||
.It NOTE_RENAME
|
||||
The file referenced by the descriptor was renamed.
|
||||
.It NOTE_REVOKE
|
||||
Access to the file was revoked via
|
||||
.Xr revoke 2
|
||||
or the underlying file system was unmounted.
|
||||
.El
|
||||
.Pp
|
||||
On return,
|
||||
.Va fflags
|
||||
contains the events which triggered the filter.
|
||||
.It EVFILT_PROC
|
||||
Takes the process ID to monitor as the identifier and the events to watch for
|
||||
in
|
||||
.Va fflags ,
|
||||
and returns when the process performs one or more of the requested events.
|
||||
If a process can normally see another process, it can attach an event to it.
|
||||
The events to monitor are:
|
||||
.Bl -tag -width XXNOTE_TRACKERR
|
||||
.It NOTE_EXIT
|
||||
The process has exited.
|
||||
The exit status will be stored in
|
||||
.Va data .
|
||||
.It NOTE_FORK
|
||||
The process has called
|
||||
.Fn fork .
|
||||
.It NOTE_EXEC
|
||||
The process has executed a new process via
|
||||
.Xr execve 2
|
||||
or similar call.
|
||||
.It NOTE_TRACK
|
||||
Follow a process across
|
||||
.Fn fork
|
||||
calls.
|
||||
The parent process will return with NOTE_TRACK set in the
|
||||
.Va fflags
|
||||
field, while the child process will return with NOTE_CHILD set in
|
||||
.Va fflags
|
||||
and the parent PID in
|
||||
.Va data .
|
||||
.It NOTE_TRACKERR
|
||||
This flag is returned if the system was unable to attach an event to
|
||||
the child process, usually due to resource limitations.
|
||||
.El
|
||||
.Pp
|
||||
On return,
|
||||
.Va fflags
|
||||
contains the events which triggered the filter.
|
||||
.It EVFILT_SIGNAL
|
||||
Takes the signal number to monitor as the identifier and returns
|
||||
when the given signal is delivered to the process.
|
||||
This coexists with the
|
||||
.Fn signal
|
||||
and
|
||||
.Fn sigaction
|
||||
facilities, and has a lower precedence.
|
||||
The filter will record
|
||||
all attempts to deliver a signal to a process, even if the signal has
|
||||
been marked as SIG_IGN.
|
||||
Event notification happens after normal
|
||||
signal delivery processing.
|
||||
.Va data
|
||||
returns the number of times the signal has occurred since the last call to
|
||||
.Fn kevent .
|
||||
This filter automatically sets the EV_CLEAR flag internally.
|
||||
.It EVFILT_TIMER
|
||||
Establishes an arbitrary timer identified by
|
||||
.Va ident .
|
||||
When adding a timer,
|
||||
.Va data
|
||||
specifies the timeout period in milliseconds.
|
||||
The timer will be periodic unless EV_ONESHOT is specified.
|
||||
On return,
|
||||
.Va data
|
||||
contains the number of times the timeout has expired since the last call to
|
||||
.Fn kevent .
|
||||
This filter automatically sets the EV_CLEAR flag internally.
|
||||
There is a system wide limit on the number of timers
|
||||
which is controlled by the
|
||||
.Va kern.kq_calloutmax
|
||||
sysctl.
|
||||
.It Dv EVFILT_NETDEV
|
||||
Takes a descriptor to a network interface as the identifier, and the events to watch for in
|
||||
.Va fflags .
|
||||
It returns, when one or more of the requested events occur on the descriptor.
|
||||
The events to monitor are:
|
||||
.Bl -tag -width XXNOTE_LINKDOWN
|
||||
.It Dv NOTE_LINKUP
|
||||
The link is up.
|
||||
.It Dv NOTE_LINKDOWN
|
||||
The link is down.
|
||||
.It Dv NOTE_LINKINV
|
||||
The link state is invalid.
|
||||
.El
|
||||
.Pp
|
||||
On return,
|
||||
.Va fflags
|
||||
contains the events which triggered the filter.
|
||||
.It Dv EVFILT_USER
|
||||
Establishes a user event identified by
|
||||
.Va ident
|
||||
which is not assosicated with any kernel mechanism but is triggered by
|
||||
user level code.
|
||||
The lower 24 bits of the
|
||||
.Va fflags
|
||||
may be used for user defined flags and manipulated using the following:
|
||||
.Bl -tag -width XXNOTE_FFLAGSMASK
|
||||
.It Dv NOTE_FFNOP
|
||||
Ignore the input
|
||||
.Va fflags .
|
||||
.It Dv NOTE_FFAND
|
||||
Bitwise AND
|
||||
.Va fflags .
|
||||
.It Dv NOTE_FFOR
|
||||
Bitwise OR
|
||||
.Va fflags .
|
||||
.It Dv NOTE_COPY
|
||||
Copy
|
||||
.Va fflags .
|
||||
.It Dv NOTE_FFCTRLMASK
|
||||
Control mask for
|
||||
.Va fflags .
|
||||
.It Dv NOTE_FFLAGSMASK
|
||||
User defined flag mask for
|
||||
.Va fflags .
|
||||
.El
|
||||
.Pp
|
||||
A user event is triggered for output with the following:
|
||||
.Bl -tag -width XXNOTE_FFLAGSMASK
|
||||
.It Dv NOTE_TRIGGER
|
||||
Cause the event to be triggered.
|
||||
.El
|
||||
.Pp
|
||||
On return,
|
||||
.Va fflags
|
||||
contains the users defined flags in the lower 24 bits.
|
||||
.El
|
||||
.Sh RETURN VALUES
|
||||
The
|
||||
.Fn kqueue
|
||||
system call
|
||||
creates a new kernel event queue and returns a file descriptor.
|
||||
If there was an error creating the kernel event queue, a value of -1 is
|
||||
returned and errno set.
|
||||
.Pp
|
||||
The
|
||||
.Fn kevent
|
||||
system call
|
||||
returns the number of events placed in the
|
||||
.Fa eventlist ,
|
||||
up to the value given by
|
||||
.Fa nevents .
|
||||
If an error occurs while processing an element of the
|
||||
.Fa changelist
|
||||
and there is enough room in the
|
||||
.Fa eventlist ,
|
||||
then the event will be placed in the
|
||||
.Fa eventlist
|
||||
with
|
||||
.Dv EV_ERROR
|
||||
set in
|
||||
.Va flags
|
||||
and the system error in
|
||||
.Va data .
|
||||
Otherwise,
|
||||
.Dv -1
|
||||
will be returned, and
|
||||
.Dv errno
|
||||
will be set to indicate the error condition.
|
||||
If the time limit expires, then
|
||||
.Fn kevent
|
||||
returns 0.
|
||||
.Sh ERRORS
|
||||
The
|
||||
.Fn kqueue
|
||||
system call fails if:
|
||||
.Bl -tag -width Er
|
||||
.It Bq Er ENOMEM
|
||||
The kernel failed to allocate enough memory for the kernel queue.
|
||||
.It Bq Er EMFILE
|
||||
The per-process descriptor table is full.
|
||||
.It Bq Er ENFILE
|
||||
The system file table is full.
|
||||
.El
|
||||
.Pp
|
||||
The
|
||||
.Fn kevent
|
||||
system call fails if:
|
||||
.Bl -tag -width Er
|
||||
.It Bq Er EACCES
|
||||
The process does not have permission to register a filter.
|
||||
.It Bq Er EFAULT
|
||||
There was an error reading or writing the
|
||||
.Va kevent
|
||||
structure.
|
||||
.It Bq Er EBADF
|
||||
The specified descriptor is invalid.
|
||||
.It Bq Er EINTR
|
||||
A signal was delivered before the timeout expired and before any
|
||||
events were placed on the kqueue for return.
|
||||
.It Bq Er EINVAL
|
||||
The specified time limit or filter is invalid.
|
||||
.It Bq Er ENOENT
|
||||
The event could not be found to be modified or deleted.
|
||||
.It Bq Er ENOMEM
|
||||
No memory was available to register the event
|
||||
or, in the special case of a timer, the maximum number of
|
||||
timers has been exceeded.
|
||||
This maximum is configurable via the
|
||||
.Va kern.kq_calloutmax
|
||||
sysctl.
|
||||
.It Bq Er ESRCH
|
||||
The specified process to attach to does not exist.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr aio_error 2 ,
|
||||
.Xr aio_read 2 ,
|
||||
.Xr aio_return 2 ,
|
||||
.Xr poll 2 ,
|
||||
.Xr read 2 ,
|
||||
.Xr select 2 ,
|
||||
.Xr sigaction 2 ,
|
||||
.Xr write 2 ,
|
||||
.Xr signal 3
|
||||
.Sh HISTORY
|
||||
The
|
||||
.Fn kqueue
|
||||
and
|
||||
.Fn kevent
|
||||
system calls first appeared in
|
||||
.Fx 4.1 .
|
||||
.Sh AUTHORS
|
||||
The
|
||||
.Fn kqueue
|
||||
system and this manual page were written by
|
||||
.An Jonathan Lemon Aq jlemon@FreeBSD.org .
|
||||
.Sh BUGS
|
||||
The
|
||||
.Dv EVFILT_NETDEV
|
||||
filter is currently only implemented for devices that use the
|
||||
.Xr miibus 4
|
||||
driver for LINKUP and LINKDOWN operations.
|
||||
Therefore, it will not work with many non-ethernet devices.
|
||||
.Pp
|
||||
The
|
||||
.Fa timeout
|
||||
value is limited to 24 hours; longer timeouts will be silently
|
||||
reinterpreted as 24 hours.
|
155
kqueue.c
Normal file
155
kqueue.c
Normal file
@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Mark Heily <mark@heily.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sys/event.h"
|
||||
#include "private.h"
|
||||
|
||||
static LIST_HEAD(,kqueue) kqlist = LIST_HEAD_INITIALIZER(&kqlist);
|
||||
static pthread_mutex_t kqlist_mtx = PTHREAD_MUTEX_INITIALIZER;
|
||||
#define kqlist_lock() pthread_mutex_lock(&kqlist_mtx)
|
||||
#define kqlist_unlock() pthread_mutex_unlock(&kqlist_mtx)
|
||||
|
||||
struct kqueue *
|
||||
kqueue_lookup(int kq)
|
||||
{
|
||||
struct kqueue *ent = NULL;
|
||||
|
||||
kqlist_lock();
|
||||
LIST_FOREACH(ent, &kqlist, entries) {
|
||||
if (ent->kq_sockfd[1] == kq)
|
||||
break;
|
||||
}
|
||||
kqlist_unlock();
|
||||
|
||||
return (ent);
|
||||
}
|
||||
|
||||
static void
|
||||
kqueue_shutdown(struct kqueue *kq)
|
||||
{
|
||||
dbg_puts("shutdown invoked\n");
|
||||
|
||||
kqlist_lock();
|
||||
LIST_REMOVE(kq, entries);
|
||||
kqlist_unlock();
|
||||
filter_unregister_all(kq);
|
||||
free(kq);
|
||||
pthread_exit(NULL);
|
||||
}
|
||||
|
||||
static void *
|
||||
kqueue_close_wait(void *arg)
|
||||
{
|
||||
struct kqueue *kq = (struct kqueue *) arg;
|
||||
struct pollfd fds[1];
|
||||
int n;
|
||||
|
||||
/* Block all signals in this thread */
|
||||
sigset_t mask;
|
||||
sigfillset(&mask);
|
||||
if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) {
|
||||
dbg_printf("sigprocmask: %s", strerror(errno));
|
||||
abort();
|
||||
}
|
||||
|
||||
/* Watch for the close(2) of the kqueue fd */
|
||||
fds[0].fd = kq->kq_sockfd[0];
|
||||
fds[0].events = POLLIN;
|
||||
|
||||
/* Almost solves a race condition when close(2) is called immediately
|
||||
after kqueue(2). Also helps prevent signal races.
|
||||
*/
|
||||
kqlist_unlock();
|
||||
|
||||
for (;;) {
|
||||
n = poll(&fds[0], 1, -1);
|
||||
if (n == 0)
|
||||
continue; /* Should never happen */
|
||||
if (n < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
dbg_printf("poll(2): %s", strerror(errno));
|
||||
abort(); //FIXME
|
||||
}
|
||||
}
|
||||
dbg_puts("kqueue: fd closed");
|
||||
|
||||
kqueue_shutdown(kq);
|
||||
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
kqueue_lock(struct kqueue *kq)
|
||||
{
|
||||
dbg_puts("kqueue_lock()");
|
||||
pthread_mutex_lock(&kq->kq_mtx);
|
||||
}
|
||||
|
||||
void
|
||||
kqueue_unlock(struct kqueue *kq)
|
||||
{
|
||||
dbg_puts("kqueue_unlock()");
|
||||
pthread_mutex_unlock(&kq->kq_mtx);
|
||||
}
|
||||
|
||||
int
|
||||
kqueue(void)
|
||||
{
|
||||
struct kqueue *kq;
|
||||
|
||||
kq = calloc(1, sizeof(*kq));
|
||||
if (kq == NULL)
|
||||
return (-1);
|
||||
pthread_mutex_init(&kq->kq_mtx, NULL);
|
||||
|
||||
if (filter_register_all(kq) < 0)
|
||||
return (-1);
|
||||
|
||||
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, kq->kq_sockfd) < 0) {
|
||||
free(kq);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
kqlist_lock();
|
||||
if (pthread_create(&kq->kq_close_tid, NULL, kqueue_close_wait, kq) != 0) {
|
||||
close(kq->kq_sockfd[0]);
|
||||
close(kq->kq_sockfd[1]);
|
||||
free(kq);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
kqlist_lock();
|
||||
LIST_INSERT_HEAD(&kqlist, kq, entries);
|
||||
kqlist_unlock();
|
||||
|
||||
dbg_printf("created kqueue: fd=%d", kq->kq_sockfd[1]);
|
||||
return (kq->kq_sockfd[1]);
|
||||
}
|
135
os/linux/signal.c
Normal file
135
os/linux/signal.c
Normal file
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Mark Heily <mark@heily.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/signalfd.h>
|
||||
|
||||
#include "sys/event.h"
|
||||
#include "../../private.h"
|
||||
|
||||
/* Highest signal number supported. POSIX standard signals are < 32 */
|
||||
#define SIGNAL_MAX 32
|
||||
|
||||
int
|
||||
evfilt_signal_init(struct filter *filt)
|
||||
{
|
||||
sigemptyset(&filt->kf_sigmask);
|
||||
filt->kf_pfd = signalfd(-1, &filt->kf_sigmask, 0);
|
||||
dbg_printf("signalfd = %d", filt->kf_pfd);
|
||||
if (filt->kf_pfd < 0)
|
||||
return (-1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
evfilt_signal_destroy(struct filter *filt)
|
||||
{
|
||||
close (filt->kf_pfd);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_signal_copyin(struct filter *filt,
|
||||
struct knote *dst, const struct kevent *src)
|
||||
{
|
||||
int rv;
|
||||
|
||||
if (src->ident >= SIGNAL_MAX) {
|
||||
dbg_printf("unsupported signal number %u", (u_int) src->ident);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (src->flags & EV_ADD && KNOTE_EMPTY(dst)) {
|
||||
memcpy(&dst->kev, src, sizeof(*src));
|
||||
dst->kev.flags |= EV_CLEAR;
|
||||
}
|
||||
if (src->flags & EV_ADD || src->flags & EV_ENABLE)
|
||||
sigaddset(&filt->kf_sigmask, src->ident);
|
||||
if (src->flags & EV_DISABLE || src->flags & EV_DELETE)
|
||||
sigdelset(&filt->kf_sigmask, src->ident);
|
||||
|
||||
rv = signalfd(filt->kf_pfd, &filt->kf_sigmask, 0);
|
||||
dbg_printf("signalfd = %d", filt->kf_pfd);
|
||||
if (rv < 0 || rv != filt->kf_pfd) {
|
||||
dbg_printf("signalfd(2): %s", strerror(errno));
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_signal_copyout(struct filter *filt,
|
||||
struct kevent *dst,
|
||||
int nevents)
|
||||
{
|
||||
struct knote *kn;
|
||||
struct signalfd_siginfo sig[MAX_KEVENT];
|
||||
int i;
|
||||
ssize_t n;
|
||||
|
||||
/* NOTE: This will consume the signals so they will not be delivered
|
||||
* to the process. This differs from kqueue(2) behavior. */
|
||||
n = read(filt->kf_pfd, &sig, nevents * sizeof(sig[0]));
|
||||
if (n < 0 || n < sizeof(sig[0])) {
|
||||
dbg_puts("invalid read from signalfd");
|
||||
return (-1);
|
||||
}
|
||||
n /= sizeof(sig[0]);
|
||||
|
||||
for (i = 0, nevents = 0; i < n; i++) {
|
||||
/* This is not an error because of this race condition:
|
||||
* 1. Signal arrives and is queued
|
||||
* 2. The kevent is deleted via kevent(..., EV_DELETE)
|
||||
* 3. The event is dequeued from the signalfd
|
||||
*/
|
||||
kn = knote_lookup(filt, sig[i].ssi_signo);
|
||||
if (kn == NULL)
|
||||
continue;
|
||||
|
||||
dbg_printf("got signal %d", sig[i].ssi_signo);
|
||||
/* TODO: dst->data should be the number of times the signal occurred */
|
||||
dst->ident = sig[i].ssi_signo;
|
||||
dst->filter = EVFILT_SIGNAL;
|
||||
dst->udata = kn->kev.udata;
|
||||
dst->flags = 0;
|
||||
dst->fflags = 0;
|
||||
dst->data = 1;
|
||||
dst++;
|
||||
nevents++;
|
||||
}
|
||||
|
||||
return (nevents);
|
||||
}
|
||||
|
||||
const struct filter evfilt_signal = {
|
||||
EVFILT_SIGNAL,
|
||||
evfilt_signal_init,
|
||||
evfilt_signal_destroy,
|
||||
evfilt_signal_copyin,
|
||||
evfilt_signal_copyout,
|
||||
};
|
149
os/linux/socket.c
Normal file
149
os/linux/socket.c
Normal file
@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Mark Heily <mark@heily.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <sys/epoll.h>
|
||||
|
||||
#include "sys/event.h"
|
||||
#include "private.h"
|
||||
|
||||
int
|
||||
evfilt_socket_init(struct filter *filt)
|
||||
{
|
||||
filt->kf_pfd = epoll_create(1);
|
||||
if (filt->kf_pfd < 0)
|
||||
return (-1);
|
||||
|
||||
dbg_printf("socket epollfd = %d", filt->kf_pfd);
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
evfilt_socket_destroy(struct filter *filt)
|
||||
{
|
||||
close(filt->kf_pfd);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_socket_copyin(struct filter *filt,
|
||||
struct knote *dst, const struct kevent *src)
|
||||
{
|
||||
struct epoll_event ev;
|
||||
int op, rv;
|
||||
|
||||
/* Determine which operation to perform */
|
||||
if (src->flags & EV_ADD && KNOTE_EMPTY(dst)) {
|
||||
op = EPOLL_CTL_ADD;
|
||||
memcpy(&dst->kev, src, sizeof(*src));
|
||||
}
|
||||
if (src->flags & EV_DELETE)
|
||||
op = EPOLL_CTL_DEL;
|
||||
if (src->flags & EV_ENABLE || src->flags & EV_DISABLE)
|
||||
op = EPOLL_CTL_MOD;
|
||||
// FIXME: probably won't work with EV_ADD | EV_DISABLE
|
||||
|
||||
/* Convert the kevent into an epoll_event */
|
||||
if (src->filter == EVFILT_READ)
|
||||
ev.events = EPOLLIN;
|
||||
else
|
||||
ev.events = EPOLLOUT;
|
||||
if (src->flags & EV_ONESHOT)
|
||||
ev.events |= EPOLLONESHOT;
|
||||
if (src->flags & EV_CLEAR)
|
||||
ev.events |= EPOLLET;
|
||||
ev.data.fd = src->ident;
|
||||
|
||||
if (src->flags & EV_DISABLE)
|
||||
ev.events = 0;
|
||||
|
||||
dbg_printf("epoll_ctl(2): epfd=%d, op=%d, fd=%d evts=%d",
|
||||
filt->kf_pfd, op, (int)src->ident, ev.events);
|
||||
rv = epoll_ctl(filt->kf_pfd, op, src->ident, &ev);
|
||||
if (rv < 0) {
|
||||
dbg_printf("epoll_ctl(2): %s", strerror(errno));
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_socket_copyout(struct filter *filt,
|
||||
struct kevent *dst,
|
||||
int nevents)
|
||||
{
|
||||
struct epoll_event epevt[MAX_KEVENT];
|
||||
struct knote *kn;
|
||||
int i, nret;
|
||||
|
||||
for (;;) {
|
||||
nret = epoll_wait(filt->kf_pfd, &epevt[0], nevents, 0);
|
||||
if (nret < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
dbg_perror("epoll_wait");
|
||||
return (-1);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0, nevents = 0; i < nret; i++) {
|
||||
kn = knote_lookup(filt, epevt[i].data.fd);
|
||||
if (kn != NULL) {
|
||||
dst->ident = kn->kev.ident;
|
||||
dst->filter = kn->kev.filter;
|
||||
dst->udata = kn->kev.udata;
|
||||
|
||||
/* FIXME: this is wrong. See the manpage */
|
||||
dst->flags = 0;
|
||||
dst->fflags = 0;
|
||||
dst->data = 0;
|
||||
|
||||
nevents++;
|
||||
dst++;
|
||||
}
|
||||
}
|
||||
|
||||
return (nevents);
|
||||
}
|
||||
|
||||
const struct filter evfilt_read = {
|
||||
EVFILT_READ,
|
||||
evfilt_socket_init,
|
||||
evfilt_socket_destroy,
|
||||
evfilt_socket_copyin,
|
||||
evfilt_socket_copyout,
|
||||
};
|
||||
|
||||
const struct filter evfilt_write = {
|
||||
EVFILT_WRITE,
|
||||
evfilt_socket_init,
|
||||
evfilt_socket_destroy,
|
||||
evfilt_socket_copyin,
|
||||
evfilt_socket_copyout,
|
||||
};
|
175
os/linux/timer.c
Normal file
175
os/linux/timer.c
Normal file
@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Mark Heily <mark@heily.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* Linux equivalents to kqueue(2) */
|
||||
#include <sys/timerfd.h>
|
||||
|
||||
#include "sys/event.h"
|
||||
#include "../../private.h"
|
||||
|
||||
static void timer_convert(struct itimerspec *dst, int src);
|
||||
|
||||
struct evfilt_data {
|
||||
int dummy;
|
||||
};
|
||||
|
||||
/*
|
||||
* Determine the smallest interval used by active timers.
|
||||
*/
|
||||
static int
|
||||
update_timeres(struct filter *filt)
|
||||
{
|
||||
struct knote *kn;
|
||||
struct itimerspec tval;
|
||||
u_int cur = filt->kf_timeres;
|
||||
|
||||
KNOTELIST_FOREACH(kn, &filt->knl) {
|
||||
if (kn->kev.data < cur)
|
||||
cur = kn->kev.data;
|
||||
}
|
||||
|
||||
if (cur == filt->kf_timeres)
|
||||
return (0);
|
||||
|
||||
dbg_printf("new timer interval = %d", cur);
|
||||
filt->kf_timeres = cur;
|
||||
|
||||
/* Convert from miliseconds to seconds+nanoseconds */
|
||||
timer_convert(&tval, cur);
|
||||
if (timerfd_settime(filt->kf_pfd, 0, &tval, NULL) < 0) {
|
||||
dbg_printf("signalfd(2): %s", strerror(errno));
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Convert milliseconds into seconds+nanoseconds */
|
||||
static void
|
||||
timer_convert(struct itimerspec *dst, int src)
|
||||
{
|
||||
struct timespec now;
|
||||
time_t x, y;
|
||||
|
||||
/* Set the interval */
|
||||
/* XXX-FIXME: this is probably horribly wrong :) */
|
||||
x = src / 1000;
|
||||
y = (src % 1000) * 1000000;
|
||||
dst->it_interval.tv_sec = x;
|
||||
dst->it_interval.tv_nsec = y;
|
||||
|
||||
/* Set the initial expiration */
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
dst->it_value.tv_sec = now.tv_sec + x;
|
||||
dst->it_value.tv_nsec = now.tv_nsec + 7;
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_timer_init(struct filter *filt)
|
||||
{
|
||||
filt->kf_pfd = timerfd_create(CLOCK_MONOTONIC, 0);
|
||||
if (filt->kf_pfd < 0)
|
||||
return (-1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
evfilt_timer_destroy(struct filter *filt)
|
||||
{
|
||||
close (filt->kf_pfd);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_timer_copyin(struct filter *filt,
|
||||
struct knote *dst, const struct kevent *src)
|
||||
{
|
||||
if (src->flags & EV_ADD && KNOTE_EMPTY(dst)) {
|
||||
memcpy(&dst->kev, src, sizeof(*src));
|
||||
dst->kev.flags |= EV_CLEAR;
|
||||
}
|
||||
if (src->flags & EV_ADD || src->flags & EV_ENABLE) {
|
||||
if (update_timeres(filt) < 0)
|
||||
return (-1);
|
||||
}
|
||||
if (src->flags & EV_DISABLE || src->flags & EV_DELETE) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_timer_copyout(struct filter *filt,
|
||||
struct kevent *dst,
|
||||
int nevents)
|
||||
{
|
||||
//struct knote *kn;
|
||||
uint64_t buf;
|
||||
int i;
|
||||
ssize_t n;
|
||||
|
||||
n = read(filt->kf_pfd, &buf, sizeof(buf));
|
||||
if (n < 0 || n < sizeof(buf)) {
|
||||
dbg_puts("invalid read from timerfd");
|
||||
return (-1);
|
||||
}
|
||||
n = 1; // KLUDGE
|
||||
|
||||
//KNOTELIST_FOREACH(kn, &filt->knl) {
|
||||
|
||||
for (i = 0, nevents = 0; i < n; i++) {
|
||||
#if FIXME
|
||||
/* Want to have multiple timers, so maybe multiple timerfds... */
|
||||
kn = knote_lookup(filt, sig[i].ssi_signo);
|
||||
if (kn == NULL)
|
||||
continue;
|
||||
|
||||
/* TODO: dst->data should be the number of times the signal occurred */
|
||||
dst->ident = sig[i].ssi_signo;
|
||||
dst->filter = EVFILT_SIGNAL;
|
||||
dst->udata = kn->kev.udata;
|
||||
dst->flags = 0;
|
||||
dst->fflags = 0;
|
||||
dst->data = 1;
|
||||
dst++;
|
||||
nevents++;
|
||||
#endif
|
||||
}
|
||||
|
||||
return (nevents);
|
||||
}
|
||||
|
||||
const struct filter evfilt_timer = {
|
||||
EVFILT_TIMER,
|
||||
evfilt_timer_init,
|
||||
evfilt_timer_destroy,
|
||||
evfilt_timer_copyin,
|
||||
evfilt_timer_copyout,
|
||||
};
|
238
os/linux/vnode.c
Normal file
238
os/linux/vnode.c
Normal file
@ -0,0 +1,238 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Mark Heily <mark@heily.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <limits.h>
|
||||
#include <sys/inotify.h>
|
||||
#include <sys/epoll.h>
|
||||
|
||||
#include "sys/event.h"
|
||||
#include "private.h"
|
||||
|
||||
#define INEVT_MASK_DUMP(attrib) \
|
||||
if (evt->mask & attrib) \
|
||||
fputs(#attrib, stdout);
|
||||
|
||||
static void
|
||||
inotify_event_dump(struct inotify_event *evt)
|
||||
{
|
||||
fputs("[BEGIN: inotify_event dump]\n", stdout);
|
||||
fprintf(stdout, " wd = %d\n", evt->wd);
|
||||
fprintf(stdout, " mask = %o (", evt->mask);
|
||||
INEVT_MASK_DUMP(IN_ACCESS);
|
||||
INEVT_MASK_DUMP(IN_MODIFY);
|
||||
INEVT_MASK_DUMP(IN_ATTRIB);
|
||||
INEVT_MASK_DUMP(IN_CLOSE_WRITE);
|
||||
INEVT_MASK_DUMP(IN_CLOSE_NOWRITE);
|
||||
INEVT_MASK_DUMP(IN_OPEN);
|
||||
INEVT_MASK_DUMP(IN_MOVED_FROM);
|
||||
INEVT_MASK_DUMP(IN_MOVED_TO);
|
||||
INEVT_MASK_DUMP(IN_CREATE);
|
||||
INEVT_MASK_DUMP(IN_DELETE);
|
||||
INEVT_MASK_DUMP(IN_DELETE_SELF);
|
||||
INEVT_MASK_DUMP(IN_MOVE_SELF);
|
||||
fputs(")\n", stdout);
|
||||
fputs("[END: inotify_event dump]\n", stdout);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
static int
|
||||
fd_to_path(char *buf, size_t bufsz, int fd)
|
||||
{
|
||||
char path[1024]; //TODO: Maxpathlen, etc.
|
||||
|
||||
if (snprintf(&path[0], sizeof(path), "/proc/%d/fd/%d", getpid(), fd) < 0)
|
||||
return (-1);
|
||||
|
||||
memset(buf, 0, bufsz);
|
||||
return (readlink(path, buf, bufsz));
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
evfilt_vnode_init(struct filter *filt)
|
||||
{
|
||||
filt->kf_pfd = inotify_init();
|
||||
dbg_printf("inotify fd = %d", filt->kf_pfd);
|
||||
if (filt->kf_pfd < 0)
|
||||
return (-1);
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
void
|
||||
evfilt_vnode_destroy(struct filter *filt)
|
||||
{
|
||||
close(filt->kf_pfd);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_vnode_copyin(struct filter *filt,
|
||||
struct knote *dst, const struct kevent *src)
|
||||
{
|
||||
char path[1024]; //FIXME: maxpathlen
|
||||
int rv;
|
||||
uint32_t mask;
|
||||
|
||||
if (src->flags & EV_DELETE) {
|
||||
dbg_puts("hi");
|
||||
if (inotify_rm_watch(filt->kf_pfd, dst->kev.data) < 0) {
|
||||
dbg_printf("inotify_rm_watch(2): %s", strerror(errno));
|
||||
return (-1);
|
||||
} else {
|
||||
return (0);
|
||||
}
|
||||
}
|
||||
|
||||
if (src->flags & EV_ADD && KNOTE_EMPTY(dst)) {
|
||||
memcpy(&dst->kev, src, sizeof(*src));
|
||||
if (fd_to_path(&path[0], sizeof(path), src->ident) < 0)
|
||||
return (-1);
|
||||
|
||||
/* Convert the fflags to the inotify mask */
|
||||
mask = 0;
|
||||
|
||||
/* FIXME: buggy inotify will not send IN_DELETE events
|
||||
if the application has the file opened.
|
||||
See: http://lists.schmorp.de/pipermail/libev/2008q4/000443.html
|
||||
Need to proccess this case during copyout.
|
||||
|
||||
Actually it seems that IN_DELETE | IN_DELETE_SELF is only
|
||||
returned when watching directories; when watching
|
||||
files, IN_ATTRIB seems to be returned only.
|
||||
*/
|
||||
if (src->fflags & NOTE_DELETE)
|
||||
mask |= IN_ATTRIB | IN_DELETE | IN_DELETE_SELF;
|
||||
|
||||
if (src->flags & EV_ONESHOT)
|
||||
mask |= IN_ONESHOT;
|
||||
#if FIXME
|
||||
if (src->flags & EV_CLEAR)
|
||||
ev.events |= EPOLLET;
|
||||
#endif
|
||||
dbg_printf("inotify_add_watch(2); inofd=%d, mask=%d, path=%s",
|
||||
filt->kf_pfd, mask, path);
|
||||
rv = inotify_add_watch(filt->kf_pfd, path, mask);
|
||||
if (rv < 0) {
|
||||
dbg_printf("inotify_add_watch(2): %s", strerror(errno));
|
||||
return (-1);
|
||||
} else {
|
||||
dbg_printf("watch descriptor = %d", rv);
|
||||
dst->kev.data = rv;
|
||||
return (0);
|
||||
}
|
||||
|
||||
}
|
||||
if (src->flags & EV_ENABLE || src->flags & EV_DISABLE) {
|
||||
abort();
|
||||
//FIXME todo
|
||||
}
|
||||
|
||||
//REFACTOR this
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_vnode_copyout(struct filter *filt,
|
||||
struct kevent *dst,
|
||||
int nevents)
|
||||
{
|
||||
struct inotify_event inevt[MAX_KEVENT];
|
||||
struct knote *kn;
|
||||
struct stat sb;
|
||||
ssize_t n;
|
||||
int i;
|
||||
|
||||
dbg_puts("draining inotify events");
|
||||
for (;;) {
|
||||
n = read(filt->kf_pfd, &inevt[0], nevents * sizeof(inevt[0]));
|
||||
if (n < 0) {
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
dbg_perror("read");
|
||||
return (-1);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
dbg_printf("read(2) from inotify wd: %zu bytes", n);
|
||||
|
||||
for (i = 0, nevents = 0; i < n; i++) {
|
||||
if (inevt[i].wd == 0)
|
||||
break;
|
||||
inotify_event_dump(&inevt[i]);
|
||||
/* FIXME: support variable-length structures.. */
|
||||
if (inevt[i].len != 0)
|
||||
abort();
|
||||
|
||||
kn = knote_lookup_data(filt, inevt[i].wd);
|
||||
if (kn != NULL) {
|
||||
kevent_dump(&kn->kev);
|
||||
dst->ident = kn->kev.ident;
|
||||
dst->filter = kn->kev.filter;
|
||||
dst->udata = kn->kev.udata;
|
||||
|
||||
/* FIXME: this is wrong. See the manpage */
|
||||
dst->flags = 0;
|
||||
dst->fflags = 0;
|
||||
dst->data = 0;
|
||||
|
||||
/* NOTE: unavoidable filesystem race here */
|
||||
if (kn->kev.fflags & EV_DELETE) {
|
||||
if (fstat(kn->kev.ident, &sb) < 0) {
|
||||
/* TODO: handle signals */
|
||||
dbg_puts("woot!");
|
||||
dst->fflags = EV_DELETE;
|
||||
} else {
|
||||
dbg_printf("link count = %zu", sb.st_nlink);
|
||||
if (sb.st_nlink == 0) {
|
||||
dbg_puts("woot! woot!");
|
||||
dst->fflags = EV_DELETE;
|
||||
} else {
|
||||
/* FIXME: not delete.. maybe ATTRIB event */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nevents++;
|
||||
dst++;
|
||||
} else {
|
||||
dbg_printf("no match for wd # %d", inevt[i].wd);
|
||||
}
|
||||
}
|
||||
|
||||
return (nevents);
|
||||
}
|
||||
|
||||
const struct filter evfilt_vnode = {
|
||||
EVFILT_VNODE,
|
||||
evfilt_vnode_init,
|
||||
evfilt_vnode_destroy,
|
||||
evfilt_vnode_copyin,
|
||||
evfilt_vnode_copyout,
|
||||
};
|
133
os/posix/signal.c
Normal file
133
os/posix/signal.c
Normal file
@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Mark Heily <mark@heily.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sys/event.h"
|
||||
#include "private.h"
|
||||
|
||||
/* Highest signal number supported. POSIX standard signals are < 32 */
|
||||
#define SIGNAL_MAX 32
|
||||
|
||||
int
|
||||
evfilt_signal_init(struct filter *filt)
|
||||
{
|
||||
return filter_socketpair(filt);
|
||||
}
|
||||
|
||||
void
|
||||
evfilt_signal_destroy(struct filter *filt)
|
||||
{
|
||||
close (filt->kf_pfd);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_signal_copyin(struct filter *filt,
|
||||
struct knote *dst, const struct kevent *src)
|
||||
{
|
||||
//int rv;
|
||||
|
||||
if (src->ident >= SIGNAL_MAX) {
|
||||
dbg_printf("unsupported signal number %u", (u_int) src->ident);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
if (src->flags & EV_ADD && KNOTE_EMPTY(dst)) {
|
||||
memcpy(&dst->kev, src, sizeof(*src));
|
||||
dst->kev.flags |= EV_CLEAR;
|
||||
}
|
||||
if (src->flags & EV_ADD || src->flags & EV_ENABLE)
|
||||
sigaddset(&filt->kf_sigmask, src->ident);
|
||||
if (src->flags & EV_DISABLE || src->flags & EV_DELETE)
|
||||
sigdelset(&filt->kf_sigmask, src->ident);
|
||||
|
||||
#if FIXME
|
||||
/* TODO */
|
||||
rv = signalfd(filt->kf_pfd, &filt->kf_sigmask, 0);
|
||||
dbg_printf("signalfd = %d", filt->kf_pfd);
|
||||
if (rv < 0 || rv != filt->kf_pfd) {
|
||||
dbg_printf("signalfd(2): %s", strerror(errno));
|
||||
return (-1);
|
||||
}
|
||||
#endif
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_signal_copyout(struct filter *filt,
|
||||
struct kevent *dst,
|
||||
int nevents)
|
||||
{
|
||||
return (-1);
|
||||
#if TODO
|
||||
struct knote *kn;
|
||||
struct signalfd_siginfo sig[MAX_KEVENT];
|
||||
int i;
|
||||
ssize_t n;
|
||||
|
||||
/* NOTE: This will consume the signals so they will not be delivered
|
||||
* to the process. This differs from kqueue(2) behavior. */
|
||||
n = read(filt->kf_pfd, &sig, nevents * sizeof(sig[0]));
|
||||
if (n < 0 || n < sizeof(sig[0])) {
|
||||
dbg_puts("invalid read from signalfd");
|
||||
return (-1);
|
||||
}
|
||||
n /= sizeof(sig[0]);
|
||||
|
||||
for (i = 0, nevents = 0; i < n; i++) {
|
||||
/* This is not an error because of this race condition:
|
||||
* 1. Signal arrives and is queued
|
||||
* 2. The kevent is deleted via kevent(..., EV_DELETE)
|
||||
* 3. The event is dequeued from the signalfd
|
||||
*/
|
||||
kn = knote_lookup(filt, sig[i].ssi_signo);
|
||||
if (kn == NULL)
|
||||
continue;
|
||||
|
||||
dbg_printf("got signal %d", sig[i].ssi_signo);
|
||||
/* TODO: dst->data should be the number of times the signal occurred */
|
||||
dst->ident = sig[i].ssi_signo;
|
||||
dst->filter = EVFILT_SIGNAL;
|
||||
dst->udata = kn->kev.udata;
|
||||
dst->flags = 0;
|
||||
dst->fflags = 0;
|
||||
dst->data = 1;
|
||||
dst++;
|
||||
nevents++;
|
||||
}
|
||||
|
||||
return (nevents);
|
||||
#endif
|
||||
}
|
||||
|
||||
const struct filter evfilt_signal = {
|
||||
EVFILT_SIGNAL,
|
||||
evfilt_signal_init,
|
||||
evfilt_signal_destroy,
|
||||
evfilt_signal_copyin,
|
||||
evfilt_signal_copyout,
|
||||
};
|
168
os/posix/timer.c
Normal file
168
os/posix/timer.c
Normal file
@ -0,0 +1,168 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Mark Heily <mark@heily.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sys/event.h"
|
||||
#include "private.h"
|
||||
|
||||
static void timer_convert(struct itimerspec *dst, int src);
|
||||
|
||||
struct evfilt_data {
|
||||
int dummy;
|
||||
};
|
||||
|
||||
/*
|
||||
* Determine the smallest interval used by active timers.
|
||||
*/
|
||||
static int
|
||||
update_timeres(struct filter *filt)
|
||||
{
|
||||
struct knote *kn;
|
||||
struct itimerspec tval;
|
||||
u_int cur = filt->kf_timeres;
|
||||
|
||||
KNOTELIST_FOREACH(kn, &filt->knl) {
|
||||
if (kn->kev.data < cur)
|
||||
cur = kn->kev.data;
|
||||
}
|
||||
|
||||
if (cur == filt->kf_timeres)
|
||||
return (0);
|
||||
|
||||
dbg_printf("new timer interval = %d", cur);
|
||||
filt->kf_timeres = cur;
|
||||
|
||||
/* Convert from miliseconds to seconds+nanoseconds */
|
||||
timer_convert(&tval, cur);
|
||||
//if (timerfd_settime(filt->kf_pfd, 0, &tval, NULL) < 0) {
|
||||
// dbg_printf("signalfd(2): %s", strerror(errno));
|
||||
// return (-1);
|
||||
// }
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Convert milliseconds into seconds+nanoseconds */
|
||||
static void
|
||||
timer_convert(struct itimerspec *dst, int src)
|
||||
{
|
||||
struct timespec now;
|
||||
time_t x, y;
|
||||
|
||||
/* Set the interval */
|
||||
/* XXX-FIXME: this is probably horribly wrong :) */
|
||||
x = src / 1000;
|
||||
y = (src % 1000) * 1000000;
|
||||
dst->it_interval.tv_sec = x;
|
||||
dst->it_interval.tv_nsec = y;
|
||||
|
||||
/* Set the initial expiration */
|
||||
clock_gettime(CLOCK_MONOTONIC, &now);
|
||||
dst->it_value.tv_sec = now.tv_sec + x;
|
||||
dst->it_value.tv_nsec = now.tv_nsec + 7;
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_timer_init(struct filter *filt)
|
||||
{
|
||||
return filter_socketpair(filt);
|
||||
}
|
||||
|
||||
void
|
||||
evfilt_timer_destroy(struct filter *filt)
|
||||
{
|
||||
close (filt->kf_pfd);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_timer_copyin(struct filter *filt,
|
||||
struct knote *dst, const struct kevent *src)
|
||||
{
|
||||
if (src->flags & EV_ADD && KNOTE_EMPTY(dst)) {
|
||||
memcpy(&dst->kev, src, sizeof(*src));
|
||||
dst->kev.flags |= EV_CLEAR;
|
||||
}
|
||||
if (src->flags & EV_ADD || src->flags & EV_ENABLE) {
|
||||
if (update_timeres(filt) < 0)
|
||||
return (-1);
|
||||
}
|
||||
if (src->flags & EV_DISABLE || src->flags & EV_DELETE) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_timer_copyout(struct filter *filt,
|
||||
struct kevent *dst,
|
||||
int nevents)
|
||||
{
|
||||
//struct knote *kn;
|
||||
uint64_t buf;
|
||||
int i;
|
||||
ssize_t n;
|
||||
|
||||
n = read(filt->kf_pfd, &buf, sizeof(buf));
|
||||
if (n < 0 || n < sizeof(buf)) {
|
||||
dbg_puts("invalid read from timerfd");
|
||||
return (-1);
|
||||
}
|
||||
n = 1; // KLUDGE
|
||||
|
||||
//KNOTELIST_FOREACH(kn, &filt->knl) {
|
||||
|
||||
for (i = 0, nevents = 0; i < n; i++) {
|
||||
#if FIXME
|
||||
/* Want to have multiple timers, so maybe multiple timerfds... */
|
||||
kn = knote_lookup(filt, sig[i].ssi_signo);
|
||||
if (kn == NULL)
|
||||
continue;
|
||||
|
||||
/* TODO: dst->data should be the number of times the signal occurred */
|
||||
dst->ident = sig[i].ssi_signo;
|
||||
dst->filter = EVFILT_SIGNAL;
|
||||
dst->udata = kn->kev.udata;
|
||||
dst->flags = 0;
|
||||
dst->fflags = 0;
|
||||
dst->data = 1;
|
||||
dst++;
|
||||
nevents++;
|
||||
#endif
|
||||
}
|
||||
|
||||
return (nevents);
|
||||
}
|
||||
|
||||
const struct filter evfilt_timer = {
|
||||
EVFILT_TIMER,
|
||||
evfilt_timer_init,
|
||||
evfilt_timer_destroy,
|
||||
evfilt_timer_copyin,
|
||||
evfilt_timer_copyout,
|
||||
};
|
67
os/posix/user.c
Normal file
67
os/posix/user.c
Normal file
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Mark Heily <mark@heily.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/queue.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sys/event.h"
|
||||
#include "private.h"
|
||||
|
||||
int
|
||||
evfilt_user_init(struct filter *filt)
|
||||
{
|
||||
return filter_socketpair(filt);
|
||||
}
|
||||
|
||||
void
|
||||
evfilt_user_destroy(struct filter *filt)
|
||||
{
|
||||
close(filt->kf_wfd); /* TODO: do this in the parent */
|
||||
return;
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_user_copyin(struct filter *filt,
|
||||
struct knote *dst, const struct kevent *src)
|
||||
{
|
||||
/* STUB */
|
||||
return (-1);
|
||||
}
|
||||
|
||||
int
|
||||
evfilt_user_copyout(struct filter *filt,
|
||||
struct kevent *dst,
|
||||
int nevents)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
const struct filter evfilt_user = {
|
||||
EVFILT_USER,
|
||||
evfilt_user_init,
|
||||
evfilt_user_destroy,
|
||||
evfilt_user_copyin,
|
||||
evfilt_user_copyout,
|
||||
};
|
103
private.h
Normal file
103
private.h
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Mark Heily <mark@heily.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef _KQUEUE_PRIVATE_H
|
||||
#define _KQUEUE_PRIVATE_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/select.h>
|
||||
#include "sys/event.h"
|
||||
|
||||
/* Maximum events returnable in a single kevent() call */
|
||||
#define MAX_KEVENT 512
|
||||
|
||||
#ifdef KQUEUE_DEBUG
|
||||
# define dbg_puts(str) fprintf(stderr, "%s\n", str)
|
||||
# define dbg_printf(fmt,...) fprintf(stderr, fmt"\n", __VA_ARGS__)
|
||||
# define dbg_perror(str) fprintf(stderr, "%s: %s\n", str, strerror(errno))
|
||||
#else
|
||||
# define dbg_puts(str) ;
|
||||
# define dbg_printf(fmt,...) ;
|
||||
# define dbg_perror(str) ;
|
||||
#endif
|
||||
|
||||
struct kqueue;
|
||||
struct kevent;
|
||||
struct evfilt_data;
|
||||
|
||||
struct knote {
|
||||
struct kevent kev;
|
||||
LIST_ENTRY(knote) entries;
|
||||
};
|
||||
LIST_HEAD(knotelist, knote);
|
||||
|
||||
/* TODO: This should be a red-black tree or a heap */
|
||||
#define KNOTELIST_INIT(knl) LIST_INIT((knl))
|
||||
#define KNOTELIST_FOREACH(ent,knl) LIST_FOREACH((ent),(knl), entries)
|
||||
#define KNOTE_INSERT(knl, ent) LIST_INSERT_HEAD((knl), (ent), entries)
|
||||
#define KNOTE_EMPTY(ent) ((ent)->kev.filter == 0)
|
||||
|
||||
struct filter {
|
||||
int kf_id;
|
||||
int (*kf_init)(struct filter *);
|
||||
void (*kf_destroy)(struct filter *);
|
||||
int (*kf_copyin)(struct filter *,
|
||||
struct knote *,
|
||||
const struct kevent *);
|
||||
int (*kf_copyout)(struct filter *, struct kevent *, int);
|
||||
int kf_pfd; /* fd to poll(2) for readiness */
|
||||
int kf_wfd; /* fd to write when an event occurs */
|
||||
u_int kf_timeres; /* timer resolution, in miliseconds */
|
||||
sigset_t kf_sigmask;
|
||||
struct evfilt_data *kf_data; /* filter-specific data */
|
||||
struct knotelist knl;
|
||||
struct kqueue *kf_kqueue;
|
||||
};
|
||||
|
||||
struct kqueue {
|
||||
int kq_sockfd[2];
|
||||
pthread_t kq_close_tid;
|
||||
struct filter kq_filt[EVFILT_SYSCOUNT];
|
||||
fd_set kq_fds;
|
||||
int kq_nfds;
|
||||
pthread_mutex_t kq_mtx;
|
||||
LIST_ENTRY(kqueue) entries;
|
||||
};
|
||||
|
||||
struct knote * knote_lookup(struct filter *, short);
|
||||
struct knote * knote_lookup_data(struct filter *filt, intptr_t);
|
||||
struct knote * knote_new(struct filter *);
|
||||
void knote_free(struct knote *);
|
||||
|
||||
struct filter * filter_lookup(struct kqueue *, short);
|
||||
int filter_socketpair(struct filter *);
|
||||
int filter_register_all(struct kqueue *);
|
||||
void filter_unregister_all(struct kqueue *);
|
||||
const char *filter_name(short);
|
||||
|
||||
int kevent_init(struct kqueue *);
|
||||
const char * kevent_dump(struct kevent *);
|
||||
int kevent_wait(struct kqueue *kq,
|
||||
struct kevent *kevent,
|
||||
int nevents,
|
||||
const struct timespec *timeout);
|
||||
void kevent_free(struct kqueue *);
|
||||
|
||||
struct kqueue * kqueue_lookup(int kq);
|
||||
void kqueue_lock(struct kqueue *kq);
|
||||
void kqueue_unlock(struct kqueue *kq);
|
||||
|
||||
#endif /* ! _KQUEUE_PRIVATE_H */
|
156
sys/event.h
Normal file
156
sys/event.h
Normal file
@ -0,0 +1,156 @@
|
||||
/*-
|
||||
* Copyright (c) 2009 Mark Heily <mark@heily.com>
|
||||
* Copyright (c) 1999,2000,2001 Jonathan Lemon <jlemon@FreeBSD.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.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
|
||||
*
|
||||
* $FreeBSD SVN Revision 197533$
|
||||
*/
|
||||
|
||||
#ifndef _SYS_EVENT_H_
|
||||
#define _SYS_EVENT_H_
|
||||
|
||||
#include <sys/queue.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
struct timespec;
|
||||
|
||||
|
||||
#define EVFILT_READ (-1)
|
||||
#define EVFILT_WRITE (-2)
|
||||
#define EVFILT_AIO (-3) /* attached to aio requests */
|
||||
#define EVFILT_VNODE (-4) /* attached to vnodes */
|
||||
#define EVFILT_PROC (-5) /* attached to struct proc */
|
||||
#define EVFILT_SIGNAL (-6) /* attached to struct proc */
|
||||
#define EVFILT_TIMER (-7) /* timers */
|
||||
#define EVFILT_NETDEV (-8) /* network devices */
|
||||
#define EVFILT_FS (-9) /* filesystem events */
|
||||
#define EVFILT_LIO (-10) /* attached to lio requests */
|
||||
#define EVFILT_USER (-11) /* User events */
|
||||
#define EVFILT_SYSCOUNT 11
|
||||
|
||||
#define EV_SET(kevp_, a, b, c, d, e, f) do { \
|
||||
struct kevent *kevp = (kevp_); \
|
||||
(kevp)->ident = (a); \
|
||||
(kevp)->filter = (b); \
|
||||
(kevp)->flags = (c); \
|
||||
(kevp)->fflags = (d); \
|
||||
(kevp)->data = (e); \
|
||||
(kevp)->udata = (f); \
|
||||
} while(0)
|
||||
|
||||
struct kevent {
|
||||
uintptr_t ident; /* identifier for this event */
|
||||
short filter; /* filter for event */
|
||||
u_short flags;
|
||||
u_int fflags;
|
||||
intptr_t data;
|
||||
void *udata; /* opaque user data identifier */
|
||||
};
|
||||
|
||||
/* actions */
|
||||
#define EV_ADD 0x0001 /* add event to kq (implies enable) */
|
||||
#define EV_DELETE 0x0002 /* delete event from kq */
|
||||
#define EV_ENABLE 0x0004 /* enable event */
|
||||
#define EV_DISABLE 0x0008 /* disable event (not reported) */
|
||||
|
||||
/* flags */
|
||||
#define EV_ONESHOT 0x0010 /* only report one occurrence */
|
||||
#define EV_CLEAR 0x0020 /* clear event state after reporting */
|
||||
#define EV_RECEIPT 0x0040 /* force EV_ERROR on success, data=0 */
|
||||
#define EV_DISPATCH 0x0080 /* disable event after reporting */
|
||||
|
||||
#define EV_SYSFLAGS 0xF000 /* reserved by system */
|
||||
#define EV_FLAG1 0x2000 /* filter-specific flag */
|
||||
|
||||
/* returned values */
|
||||
#define EV_EOF 0x8000 /* EOF detected */
|
||||
#define EV_ERROR 0x4000 /* error, data contains errno */
|
||||
|
||||
/*
|
||||
* data/hint flags/masks for EVFILT_USER
|
||||
*
|
||||
* On input, the top two bits of fflags specifies how the lower twenty four
|
||||
* bits should be applied to the stored value of fflags.
|
||||
*
|
||||
* On output, the top two bits will always be set to NOTE_FFNOP and the
|
||||
* remaining twenty four bits will contain the stored fflags value.
|
||||
*/
|
||||
#define NOTE_FFNOP 0x00000000 /* ignore input fflags */
|
||||
#define NOTE_FFAND 0x40000000 /* AND fflags */
|
||||
#define NOTE_FFOR 0x80000000 /* OR fflags */
|
||||
#define NOTE_FFCOPY 0xc0000000 /* copy fflags */
|
||||
#define NOTE_FFCTRLMASK 0xc0000000 /* masks for operations */
|
||||
#define NOTE_FFLAGSMASK 0x00ffffff
|
||||
|
||||
#define NOTE_TRIGGER 0x01000000 /* Cause the event to be
|
||||
triggered for output. */
|
||||
|
||||
/*
|
||||
* data/hint flags for EVFILT_{READ|WRITE}
|
||||
*/
|
||||
#define NOTE_LOWAT 0x0001 /* low water mark */
|
||||
|
||||
/*
|
||||
* data/hint flags for EVFILT_VNODE
|
||||
*/
|
||||
#define NOTE_DELETE 0x0001 /* vnode was removed */
|
||||
#define NOTE_WRITE 0x0002 /* data contents changed */
|
||||
#define NOTE_EXTEND 0x0004 /* size increased */
|
||||
#define NOTE_ATTRIB 0x0008 /* attributes changed */
|
||||
#define NOTE_LINK 0x0010 /* link count changed */
|
||||
#define NOTE_RENAME 0x0020 /* vnode was renamed */
|
||||
#define NOTE_REVOKE 0x0040 /* vnode access was revoked */
|
||||
|
||||
/*
|
||||
* data/hint flags for EVFILT_PROC
|
||||
*/
|
||||
#define NOTE_EXIT 0x80000000 /* process exited */
|
||||
#define NOTE_FORK 0x40000000 /* process forked */
|
||||
#define NOTE_EXEC 0x20000000 /* process exec'd */
|
||||
#define NOTE_PCTRLMASK 0xf0000000 /* mask for hint bits */
|
||||
#define NOTE_PDATAMASK 0x000fffff /* mask for pid */
|
||||
|
||||
/* additional flags for EVFILT_PROC */
|
||||
#define NOTE_TRACK 0x00000001 /* follow across forks */
|
||||
#define NOTE_TRACKERR 0x00000002 /* could not track child */
|
||||
#define NOTE_CHILD 0x00000004 /* am a child process */
|
||||
|
||||
/*
|
||||
* data/hint flags for EVFILT_NETDEV
|
||||
*/
|
||||
#define NOTE_LINKUP 0x0001 /* link is up */
|
||||
#define NOTE_LINKDOWN 0x0002 /* link is down */
|
||||
#define NOTE_LINKINV 0x0004 /* link state is invalid */
|
||||
|
||||
__BEGIN_DECLS
|
||||
int kqueue(void);
|
||||
int kevent(int kq, const struct kevent *changelist, int nchanges,
|
||||
struct kevent *eventlist, int nevents,
|
||||
const struct timespec *timeout);
|
||||
__END_DECLS
|
||||
|
||||
#endif /* !_SYS_EVENT_H_ */
|
||||
|
467
test.c
Normal file
467
test.c
Normal file
@ -0,0 +1,467 @@
|
||||
/*
|
||||
* Copyright (c) 2009 Mark Heily <mark@heily.com>
|
||||
*
|
||||
* Permission to use, copy, modify, and distribute this software for any
|
||||
* purpose with or without fee is hereby granted, provided that the above
|
||||
* copyright notice and this permission notice appear in all copies.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifdef UNIT_TEST
|
||||
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "sys/event.h"
|
||||
|
||||
char *cur_test_id = "undef";
|
||||
int kqfd;
|
||||
int sockfd[2];
|
||||
int vnode_fd;
|
||||
|
||||
/* In kevent.c */
|
||||
const char * kevent_dump(struct kevent *);
|
||||
|
||||
#define KEV_CMP(kev,_ident,_filter,_flags) do { \
|
||||
if (kev.ident != (_ident) || \
|
||||
kev.filter != (_filter) || \
|
||||
kev.flags != (_flags)) \
|
||||
err(1, "kevent mismatch: got %s but expecting [%d,%d,%d]", \
|
||||
kevent_dump(&kev),\
|
||||
(int)kev.ident, kev.filter, kev.flags);\
|
||||
} while (0);
|
||||
|
||||
/* Checks if any events are pending, which is an error. */
|
||||
void
|
||||
test_no_kevents(void)
|
||||
{
|
||||
int nfds;
|
||||
struct timespec timeo;
|
||||
struct kevent kev;
|
||||
|
||||
puts("confirming that there are no events pending");
|
||||
memset(&timeo, 0, sizeof(timeo));
|
||||
nfds = kevent(kqfd, NULL, 0, &kev, 1, &timeo);
|
||||
if (nfds != 0) {
|
||||
puts(kevent_dump(&kev));
|
||||
errx(1, "%d event(s) pending, but none expected:", nfds);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
test_begin(const char *func)
|
||||
{
|
||||
static int testnum = 1;
|
||||
cur_test_id = (char *) func;
|
||||
printf("\n\nTest %d: %s\n", testnum++, func);
|
||||
}
|
||||
|
||||
void
|
||||
success(const char *func)
|
||||
{
|
||||
printf("%-70s %s\n", func, "passed");
|
||||
}
|
||||
|
||||
void
|
||||
test_kqueue(void)
|
||||
{
|
||||
test_begin("kqueue()");
|
||||
if ((kqfd = kqueue()) < 0)
|
||||
err(1, "kqueue()");
|
||||
test_no_kevents();
|
||||
success("kqueue()");
|
||||
}
|
||||
|
||||
void
|
||||
test_kqueue_close(void)
|
||||
{
|
||||
test_begin("close(kq)");
|
||||
if (close(kqfd) < 0)
|
||||
err(1, "close()");
|
||||
success("kqueue_close()");
|
||||
}
|
||||
|
||||
void
|
||||
test_kevent_socket_add(void)
|
||||
{
|
||||
const char *test_id = "kevent(EVFILT_READ, EV_ADD)";
|
||||
struct kevent kev;
|
||||
|
||||
test_begin(test_id);
|
||||
EV_SET(&kev, sockfd[0], EVFILT_READ, EV_ADD, 0, 0, &sockfd[0]);
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(1, "%s", test_id);
|
||||
|
||||
success(test_id);
|
||||
}
|
||||
|
||||
void
|
||||
test_kevent_socket_get(void)
|
||||
{
|
||||
const char *test_id = "kevent(EVFILT_READ) wait";
|
||||
char buf[1];
|
||||
struct kevent kev;
|
||||
int nfds;
|
||||
|
||||
test_begin(test_id);
|
||||
|
||||
if (write(sockfd[1], ".", 1) < 1)
|
||||
err(1, "write(2)");
|
||||
|
||||
nfds = kevent(kqfd, NULL, 0, &kev, 1, NULL);
|
||||
if (nfds < 1)
|
||||
err(1, "%s", test_id);
|
||||
KEV_CMP(kev, sockfd[0], EVFILT_READ, 0);
|
||||
|
||||
/* Drain the read buffer, then make sure there are no more events. */
|
||||
puts("draining the read buffer");
|
||||
if (read(sockfd[0], &buf[0], 1) < 1)
|
||||
err(1, "read(2)");
|
||||
test_no_kevents();
|
||||
|
||||
success(test_id);
|
||||
}
|
||||
|
||||
void
|
||||
test_kevent_socket_disable(void)
|
||||
{
|
||||
const char *test_id = "kevent(EVFILT_READ, EV_DISABLE)";
|
||||
struct kevent kev;
|
||||
|
||||
test_begin(test_id);
|
||||
|
||||
EV_SET(&kev, sockfd[0], EVFILT_READ, EV_DISABLE, 0, 0, &sockfd[0]);
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(1, "%s", test_id);
|
||||
|
||||
puts("filling the read buffer");
|
||||
if (write(sockfd[1], ".", 1) < 1)
|
||||
err(1, "write(2)");
|
||||
test_no_kevents();
|
||||
|
||||
success(test_id);
|
||||
}
|
||||
|
||||
void
|
||||
test_kevent_socket_enable(void)
|
||||
{
|
||||
const char *test_id = "kevent(EVFILT_READ, EV_ENABLE)";
|
||||
struct kevent kev;
|
||||
int nfds;
|
||||
|
||||
test_begin(test_id);
|
||||
|
||||
EV_SET(&kev, sockfd[0], EVFILT_READ, EV_ENABLE, 0, 0, &sockfd[0]);
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(1, "%s", test_id);
|
||||
|
||||
nfds = kevent(kqfd, NULL, 0, &kev, 1, NULL);
|
||||
if (nfds < 1)
|
||||
err(1, "%s", test_id);
|
||||
KEV_CMP(kev, sockfd[0], EVFILT_READ, 0);
|
||||
|
||||
success(test_id);
|
||||
}
|
||||
|
||||
void
|
||||
test_kevent_socket_del(void)
|
||||
{
|
||||
const char *test_id = "kevent(EVFILT_READ, EV_DELETE)";
|
||||
struct kevent kev;
|
||||
|
||||
test_begin(test_id);
|
||||
|
||||
EV_SET(&kev, sockfd[0], EVFILT_READ, EV_DELETE, 0, 0, &sockfd[0]);
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(1, "%s", test_id);
|
||||
|
||||
puts("filling the read buffer");
|
||||
if (write(sockfd[1], ".", 1) < 1)
|
||||
err(1, "write(2)");
|
||||
test_no_kevents();
|
||||
|
||||
success(test_id);
|
||||
}
|
||||
|
||||
void
|
||||
test_kevent_signal_add(void)
|
||||
{
|
||||
const char *test_id = "kevent(EVFILT_SIGNAL, EV_ADD)";
|
||||
struct kevent kev;
|
||||
|
||||
test_begin(test_id);
|
||||
|
||||
EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD, 0, 0, &sockfd[0]);
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(1, "%s", test_id);
|
||||
|
||||
success(test_id);
|
||||
}
|
||||
|
||||
void
|
||||
test_kevent_signal_get(void)
|
||||
{
|
||||
const char *test_id = "kevent(get signal)";
|
||||
struct kevent kev;
|
||||
int nfds;
|
||||
|
||||
test_begin(test_id);
|
||||
|
||||
/* Block SIGUSR1, then send it to ourselves */
|
||||
sigset_t mask;
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, SIGUSR1);
|
||||
if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
|
||||
err(1, "sigprocmask");
|
||||
if (kill(getpid(), SIGUSR1) < 0)
|
||||
err(1, "kill");
|
||||
|
||||
nfds = kevent(kqfd, NULL, 0, &kev, 1, NULL);
|
||||
if (nfds < 1)
|
||||
err(1, "test failed: %s, retval %d", test_id, nfds);
|
||||
if (kev.ident != SIGUSR1 ||
|
||||
kev.filter != EVFILT_SIGNAL ||
|
||||
kev.flags != 0)
|
||||
err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)",
|
||||
test_id, (unsigned int)kev.ident, kev.filter, kev.flags);
|
||||
//FIXME: test kev->flags, fflags, data
|
||||
|
||||
success(test_id);
|
||||
}
|
||||
|
||||
void
|
||||
test_kevent_signal_disable(void)
|
||||
{
|
||||
const char *test_id = "kevent(EVFILT_SIGNAL, EV_DISABLE)";
|
||||
struct kevent kev;
|
||||
|
||||
test_begin(test_id);
|
||||
|
||||
EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_DISABLE, 0, 0, &sockfd[0]);
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(1, "%s", test_id);
|
||||
|
||||
/* Block SIGUSR1, then send it to ourselves */
|
||||
sigset_t mask;
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, SIGUSR1);
|
||||
if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
|
||||
err(1, "sigprocmask");
|
||||
if (kill(getpid(), SIGUSR1) < 0)
|
||||
err(1, "kill");
|
||||
|
||||
test_no_kevents();
|
||||
|
||||
success(test_id);
|
||||
}
|
||||
|
||||
void
|
||||
test_kevent_signal_enable(void)
|
||||
{
|
||||
const char *test_id = "kevent(EVFILT_SIGNAL, EV_ENABLE)";
|
||||
struct kevent kev;
|
||||
int nfds;
|
||||
|
||||
test_begin(test_id);
|
||||
|
||||
EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ENABLE, 0, 0, &sockfd[0]);
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(1, "%s", test_id);
|
||||
|
||||
/* Block SIGUSR1, then send it to ourselves */
|
||||
sigset_t mask;
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, SIGUSR1);
|
||||
if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
|
||||
err(1, "sigprocmask");
|
||||
if (kill(getpid(), SIGUSR1) < 0)
|
||||
err(1, "kill");
|
||||
|
||||
|
||||
nfds = kevent(kqfd, NULL, 0, &kev, 1, NULL);
|
||||
if (nfds < 1)
|
||||
err(1, "test failed: %s, retval %d", test_id, nfds);
|
||||
if (kev.ident != SIGUSR1 ||
|
||||
kev.filter != EVFILT_SIGNAL ||
|
||||
kev.flags != 0)
|
||||
err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)",
|
||||
test_id, (unsigned int)kev.ident, kev.filter, kev.flags);
|
||||
|
||||
success(test_id);
|
||||
}
|
||||
|
||||
void
|
||||
test_kevent_signal_del(void)
|
||||
{
|
||||
const char *test_id = "kevent(EVFILT_SIGNAL, EV_DELETE)";
|
||||
struct kevent kev;
|
||||
|
||||
test_begin(test_id);
|
||||
|
||||
/* Delete the kevent */
|
||||
EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_DELETE, 0, 0, &sockfd[0]);
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(1, "%s", test_id);
|
||||
|
||||
/* Block SIGUSR1, then send it to ourselves */
|
||||
sigset_t mask;
|
||||
sigemptyset(&mask);
|
||||
sigaddset(&mask, SIGUSR1);
|
||||
if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
|
||||
err(1, "sigprocmask");
|
||||
if (kill(getpid(), SIGUSR1) < 0)
|
||||
err(1, "kill");
|
||||
|
||||
test_no_kevents();
|
||||
success(test_id);
|
||||
}
|
||||
|
||||
void
|
||||
test_kevent_vnode_add(void)
|
||||
{
|
||||
const char *test_id = "kevent(EVFILT_VNODE, EV_ADD)";
|
||||
const char *testfile = "/tmp/kqueue-test.tmp";
|
||||
struct kevent kev;
|
||||
|
||||
test_begin(test_id);
|
||||
|
||||
system("touch /tmp/kqueue-test.tmp");
|
||||
vnode_fd = open(testfile, O_RDONLY);
|
||||
if (vnode_fd < 0)
|
||||
err(1, "open of %s", testfile);
|
||||
else
|
||||
printf("vnode_fd = %d\n", vnode_fd);
|
||||
|
||||
EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_ADD, NOTE_DELETE, 0, &sockfd[0]);
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(1, "%s", test_id);
|
||||
|
||||
// XXX-causes an event..
|
||||
close(vnode_fd);
|
||||
|
||||
success(test_id);
|
||||
}
|
||||
|
||||
void
|
||||
test_kevent_vnode_get(void)
|
||||
{
|
||||
const char *test_id = "kevent(EVFILT_VNODE, get)";
|
||||
struct kevent kev;
|
||||
int nfds;
|
||||
|
||||
test_begin(test_id);
|
||||
|
||||
if (unlink("/tmp/kqueue-test.tmp") < 0)
|
||||
err(1, "unlink");
|
||||
|
||||
nfds = kevent(kqfd, NULL, 0, &kev, 1, NULL);
|
||||
if (nfds < 1)
|
||||
err(1, "%s", test_id);
|
||||
if (kev.ident != vnode_fd ||
|
||||
kev.filter != EVFILT_VNODE ||
|
||||
kev.fflags != NOTE_DELETE)
|
||||
err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)",
|
||||
test_id, (unsigned int)kev.ident, kev.filter, kev.flags);
|
||||
|
||||
success(test_id);
|
||||
}
|
||||
|
||||
void
|
||||
test_kevent_vnode_del(void)
|
||||
{
|
||||
const char *test_id = "kevent(EVFILT_VNODE, EV_DELETE)";
|
||||
struct kevent kev;
|
||||
|
||||
test_begin(test_id);
|
||||
|
||||
EV_SET(&kev, vnode_fd, EVFILT_VNODE, EV_DELETE, 0, 0, &sockfd[0]);
|
||||
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
|
||||
err(1, "%s", test_id);
|
||||
|
||||
success(test_id);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int test_socket = 1;
|
||||
int test_signal = 0;//XXX-FIXME
|
||||
int test_vnode = 1;
|
||||
int test_timer = 1;
|
||||
|
||||
while (argc) {
|
||||
if (strcmp(argv[0], "--no-socket") == 0)
|
||||
test_socket = 0;
|
||||
if (strcmp(argv[0], "--no-timer") == 0)
|
||||
test_timer = 0;
|
||||
if (strcmp(argv[0], "--no-signal") == 0)
|
||||
test_signal = 0;
|
||||
if (strcmp(argv[0], "--no-vnode") == 0)
|
||||
test_vnode = 0;
|
||||
argv++;
|
||||
argc--;
|
||||
}
|
||||
|
||||
/* Create a connected pair of full-duplex sockets for testing socket events */
|
||||
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd) < 0)
|
||||
abort();
|
||||
|
||||
test_kqueue();
|
||||
|
||||
if (test_socket) {
|
||||
test_kevent_socket_add();
|
||||
test_kevent_socket_get();
|
||||
test_kevent_socket_disable();
|
||||
test_kevent_socket_enable();
|
||||
test_kevent_socket_del();
|
||||
}
|
||||
|
||||
if (test_signal) {
|
||||
test_kevent_signal_add();
|
||||
test_kevent_signal_get();
|
||||
test_kevent_signal_disable();
|
||||
test_kevent_signal_enable();
|
||||
test_kevent_signal_del();
|
||||
}
|
||||
|
||||
if (test_vnode) {
|
||||
test_kevent_vnode_add();
|
||||
#if ! FIXME
|
||||
//broken, hangs on epoll of kq->pfd
|
||||
test_kevent_vnode_get();
|
||||
#endif
|
||||
test_kevent_vnode_del();
|
||||
}
|
||||
|
||||
if (test_timer) {
|
||||
}
|
||||
|
||||
test_kqueue_close();
|
||||
|
||||
puts("all tests completed.");
|
||||
return (0);
|
||||
}
|
||||
|
||||
#else /* UNIT_TEST */
|
||||
|
||||
void __kqueue_dummy(void)
|
||||
{
|
||||
/* STUB */
|
||||
}
|
||||
|
||||
#endif /* ! UNIT_TEST */
|
Loading…
x
Reference in New Issue
Block a user