git-svn-id: svn://svn.code.sf.net/p/libkqueue/code/tags/REL_0_4@201 fb4e3144-bc1c-4b72-a658-5bcd248dd7f7
This commit is contained in:
mheily 2010-02-10 02:06:40 +00:00
parent 443e157e1f
commit b6d85c3ec8
41 changed files with 2490 additions and 673 deletions

View File

@ -1,3 +1,16 @@
2009-12-26 v0.4 r133
------------------------------------------------------------------------
* Incomplete and experimental support for Solaris added.
* Lots of work on the test suite.
* Replace the buggy GC thread with an event-based alternative.
* Do not implicitly set EV_CLEAR in the EVFILT_USER filter.
* Adjust the eventlist when EV_RECEIPT causes it to be modified.
2009-11-10 v0.3 r84
------------------------------------------------------------------------

View File

@ -16,19 +16,28 @@
REPOSITORY=svn+ssh://mark.heily.com/$$HOME/svn/$(PROGRAM)
DIST=heily.com:$$HOME/public_html/$(PROGRAM)/dist
DISTFILE=$(PROGRAM)-$(VERSION).tar.gz
include config.mk
build:
$(CC) $(CFLAGS) -c $(SOURCES)
ar rcs libkqueue.a *.o
gcc -shared -Wl,-soname,libkqueue.so -o libkqueue.so *.o
.PHONY :: install uninstall check dist dist-upload publish-www clean merge distclean fresh-build rpm
install:
%.o: %.c $(DEPS)
$(CC) -c -o $@ $(CFLAGS) $<
$(PROGRAM).so: $(OBJS)
$(AR) rcs $(PROGRAM).a $(OBJS)
$(LD) $(LDFLAGS) -o $(PROGRAM).so $(OBJS) $(LDADD)
all: $(PROGRAM).so
install: $(PROGRAM).so
$(INSTALL) -d -m 755 $(INCLUDEDIR)/kqueue/sys
$(INSTALL) -m 644 include/sys/event.h $(INCLUDEDIR)/kqueue/sys/event.h
$(INSTALL) -d -m 755 $(LIBDIR)
$(INSTALL) -m 644 libkqueue.so $(LIBDIR)/libkqueue.so
$(INSTALL) -m 644 $(PROGRAM).so $(LIBDIR)
$(INSTALL) -m 644 $(PROGRAM).la $(LIBDIR)
$(INSTALL) -m 644 $(PROGRAM).a $(LIBDIR)
$(INSTALL) -d -m 755 $(LIBDIR)/pkgconfig
$(INSTALL) -m 644 libkqueue.pc $(LIBDIR)/pkgconfig
$(INSTALL) -d -m 755 $(MANDIR)/man2
@ -46,17 +55,22 @@ uninstall:
check:
cd test && ./configure && make check
dist:
$(DISTFILE): $(OBJS)
cd test && make distclean || true
mkdir $(PROGRAM)-$(VERSION)
cp Makefile ChangeLog configure config.inc \
$(MANS) $(EXTRA_DIST) \
$(PROGRAM)-$(VERSION)
cp -R $(SUBDIRS) $(PROGRAM)-$(VERSION)
rm -rf `find $(PROGRAM)-$(VERSION) -type d -name .svn`
rm -rf `find $(PROGRAM)-$(VERSION) -type d -name .svn -o -name .libs`
cd $(PROGRAM)-$(VERSION) && rm $(OBJS)
tar zcf $(PROGRAM)-$(VERSION).tar.gz $(PROGRAM)-$(VERSION)
rm -rf $(PROGRAM)-$(VERSION)
dist:
rm -f $(DISTFILE)
make $(DISTFILE)
dist-upload: dist
scp $(PROGRAM)-$(VERSION).tar.gz $(DIST)
@ -64,7 +78,14 @@ publish-www:
rm ~/public_html/libkqueue/*.html ; cp -R www/*.html ~/public_html/libkqueue/
clean:
rm -f a.out *.a *.o *.so
rm -f *.a $(OBJS) *.so
cd test && make clean || true
fresh-build:
rm -rf /tmp/$(PROGRAM)-testbuild
svn co svn://mark.heily.com/libkqueue/trunk /tmp/$(PROGRAM)-testbuild
cd /tmp/$(PROGRAM)-testbuild && ./configure && make check
rm -rf /tmp/$(PROGRAM)-testbuild
merge:
svn diff $(REPOSITORY)/branches/stable $(REPOSITORY)/trunk | gvim -
@ -73,4 +94,19 @@ merge:
echo "ok"
distclean: clean
rm -f *.tar.gz config.mk config.h libkqueue.pc
rm -f *.tar.gz config.mk config.h $(PROGRAM).pc $(PROGRAM).la rpm.spec
rpm: clean $(DISTFILE)
rm -rf rpm *.rpm *.deb
mkdir -p rpm/BUILD rpm/RPMS rpm/SOURCES rpm/SPECS rpm/SRPMS
mkdir -p rpm/RPMS/i386 rpm/RPMS/x86_64
cp $(DISTFILE) rpm/SOURCES
rpmbuild -bb rpm.spec
mv ./rpm/RPMS/* .
rm -rf rpm
rmdir i386 x86_64 # WORKAROUND: These aren't supposed to exist
fakeroot alien --scripts *.rpm
debug-install:
./configure --prefix=/usr --debug=yes
make clean && make && sudo make install

View File

@ -1,18 +1,31 @@
program="libkqueue"
version="0.3"
version="0.4"
cflags="-fPIC -I./include -I./src/common -Wall -Werror"
sources="src/common/*.c"
sources="src/common/filter.c src/common/knote.c
src/common/kevent.c src/common/kqueue.c"
libdepends=""
deps="src/common/private.h"
mans="kqueue.2"
headers="src/common/private.h"
extra_dist="*.in"
subdirs="src include test"
# Package metadata
pkg_summary="Emulates the kqueue and kevent system calls"
pkg_description="Emulates the kqueue and kevent system calls"
license="BSD"
author="Mark Heily"
pre_configure_hook() {
if [ "$debug" = "yes" ] ; then
cflags="$cflags -DKQUEUE_DEBUG"
cflags="$cflags -g3 -O0 -DKQUEUE_DEBUG"
else
cflags="$cflags -g -O2"
fi
optional_headers="err.h"
libdepends=" -L$libdir"
if [ $target = "linux" ] ; then
libdepends="$libdepends -lpthread -lrt"
required_headers="sys/epoll.h sys/inotify.h
@ -22,5 +35,26 @@ pre_configure_hook() {
post_configure_hook() {
finalize target "$target"
sources="$sources src/$target/*.c"
evfilt_signal="src/$target/signal.c"
evfilt_proc="src/$target/proc.c"
evfilt_socket="src/$target/socket.c"
evfilt_timer="src/$target/timer.c"
evfilt_user="src/$target/user.c"
evfilt_vnode="src/$target/vnode.c"
if [ $target = "linux" ] ; then
if [ "$have_sys_signalfd_h" != "yes" ] ; then
evfilt_signal="src/posix/signal.c"
fi
if [ "$have_sys_timerfd_h" != "yes" ] ; then
evfilt_timer="" # TODO: "src/posix/timer.c"
fi
if [ "$have_sys_eventfd_h" != "yes" ] ; then
evfilt_user="" # TODO: "src/posix/user.c"
fi
fi
sources="$sources src/$target/hook.c $evfilt_signal $evfilt_proc
$evfilt_socket $evfilt_timer $evfilt_user $evfilt_vnode"
}

150
configure vendored
View File

@ -5,8 +5,8 @@ c_exports="program version target cflags"
make_exports="program version target \
prefix libdir includedir mandir \
cflags ldflags ldadd libdepends \
sources mans headers extra_dist subdirs \
install"
sources objs deps mans headers extra_dist subdirs \
cc cpp ld ar install"
required_headers=
optional_headers=
@ -19,12 +19,17 @@ post_configure_hook() {
return
}
. ./config.inc
export_to_make() {
for id in $*
do
uc_id=`echo $id | tr 'a-z' 'A-Z'`;
# Prepend $DESTDIR to installation directories
case "$id" in
prefix|libdir|includedir|mandir)
eval "$id=\"\\\$\\\$DESTDIR\$$id\""
esac
uc_id=`echo $id | $tr '[:lower:]' '[:upper:]'`;
eval "echo \"$uc_id=\"\$$id\"\" >> config.mk"
done
}
@ -32,12 +37,13 @@ export_to_make() {
export_to_c() {
for id in $*
do
uc_id=`echo $id | tr 'a-z' 'A-Z'`;
uc_id=`echo $id | $tr '[:lower:]' '[:upper:]'`;
eval "echo \"#define $uc_id \\\"\$$id\\\"\" >> config.h"
done
}
finalize() {
uc_id=`echo \"$1\" | $tr '[:lower:]' '[:upper:]'`;
eval "if [ \"\$$1\" = \"\" ] ; then $1=\"$2\" ; fi"
}
@ -51,10 +57,19 @@ process_argv() {
done
}
process_env() {
test -n "$CC" && cc="$CC"
test -n "$CPP" && cpp="$CPP"
test -n "$CPPFLAGS" && cppflags="$CPPFLAGS"
test -n "$CFLAGS" && cflags="$CFLAGS"
test -n "$LD" && ld="$LD"
test -n "$LDFLAGS" && ldflags="$LDFLAGS"
test -n "$AR" && ar="$AR"
}
check_header() {
sym=`echo "have_$1" | sed 's,[./],_,g'`
uc_sym=`echo "$sym" | tr a-z A-Z`
uc_sym=`echo "$sym" | $tr '[:lower:]' '[:upper:]'`;
path=$1
printf "checking for $path.. "
@ -82,8 +97,8 @@ check_symbol() {
header=$1
symbol=$2
uc_symbol=`echo "HAVE_$symbol" | tr a-z A-Z | sed 's,[./],_,g'`
lc_symbol=`echo "have_$symbol" | tr A-Z a-z | sed 's,[./],_,g'`
uc_symbol=`echo "HAVE_$symbol" | $tr '[:lower:]' '[:upper:]' | sed 's,[./],_,g'`
lc_symbol=`echo "have_$symbol" | $tr '[:upper:]' '[:lower:]' | sed 's,[./],_,g'`
if [ -f "$header" ] ; then
path="$header"
@ -119,17 +134,101 @@ check_install() {
echo "$install"
}
check_target() {
printf "checking operating system type.. "
default_target=`uname -s | $tr '[:upper:]' '[:lower:]'`
if [ "$default_target" = "sunos" ] ; then
default_target="solaris"
fi
finalize target "$default_target"
echo "$target"
}
check_compiler() {
printf "checking for a C compiler.. "
if [ "`uname -s`" = "SunOS" ] ; then
default_cc="/usr/sfw/bin/gcc"
else
default_cc="/usr/bin/cc"
fi
finalize cc "$default_cc"
echo "$cc"
}
check_linker() {
printf "checking for a suitable linker.. "
# Disabled due to problems with ld(1) under Linux
#if [ "`uname -s`" = "SunOS" ] ; then
# default_ld="/usr/sfw/bin/gld"
#else
# default_ld="/usr/bin/ld"
#fi
#ldflags="-shared -export-dynamic -soname $program.so $ldflags"
# Workaround for "hidden symbol <foo> is referenced by DSO" linker error
# seen when compiling libdispatch.
# Appears to be a problem with GCC 4.0 and binutils
#
default_ld="$cc"
ldflags="-shared -Wl,-soname,$program.so $ldflags"
finalize ld "$default_ld"
echo "$ld"
}
check_archiver() {
printf "checking for a suitable archiver.. "
if [ "`uname -s`" = "SunOS" ] ; then
default_ar="/usr/sfw/bin/gar"
else
default_ar="/usr/bin/ar"
fi
finalize ar "$default_ar"
echo "$ar"
}
err() {
echo "*** ERROR *** $*"
exit 1
}
subst_vars() {
outfile=$1
if [ ! -f "${outfile}.in" ] ; then
return
fi
echo "Creating $outfile"
rm -f $outfile
sed -e "
s,@@PROGRAM@@,$program,g;
s,@@VERSION@@,$version,g;
s,@@PREFIX@@,$prefix,g;
s,@@LIBDIR@@,$libdir,g;
s,@@INCLUDEDIR@@,$includedir,g;
s,@@MANDIR@@,$mandir,g;
s,@@LIBDEPENDS@@,$libdepends,g;
s,@@PKG_SUMMARY@@,$pkg_summary,g;
s,@@PKG_DESCRIPTION@@,$pkg_description,g;
s,@@LICENSE@@,$license,g;
s,@@AUTHOR@@,$author,g;
" < ${outfile}.in > $outfile
chmod 400 $outfile
}
#######################################################################
#
# MAIN()
#
#######################################################################
# Workaround for Solaris "Bad string" issue when LOCALE is undefined
tr="/usr/bin/tr"
test -f /usr/xpg4/bin/tr && tr="/usr/xpg4/bin/tr"
. ./config.inc
# Initialize the output files
#
for output_file in config.mk $program.pc
@ -141,10 +240,16 @@ rm -f config.h
echo "/* AUTOMATICALLY GENERATED -- DO NOT EDIT */" > config.h
process_argv "$*"
process_env
check_target
check_compiler
check_linker
check_archiver
check_install
finalize program "$program"
finalize version "$version"
finalize target `uname -s | tr A-Z a-z`
finalize prefix "/usr/local"
finalize libdir "${prefix}/lib"
finalize includedir "${prefix}/include"
@ -153,10 +258,7 @@ finalize cflags "$cflags"
finalize libdepends "$libdepends"
finalize ldadd ""
finalize ldflags ""
finalize cc "/usr/bin/cc"
echo "checking operating system type.. $target"
check_install
finalize deps ""
pre_configure_hook
@ -168,22 +270,16 @@ check_headers $optional_headers
post_configure_hook
if [ -f "$program.pc.in" ] ; then
echo "Creating $program.pc"
sed -e "
s,@@PROGRAM@@,$program,g;
s,@@VERSION@@,$version,g;
s,@@PREFIX@@,$prefix,g;
s,@@LIBDIR@@,$libdir,g;
s,@@INCLUDEDIR@@,$includedir,g;
s,@@MANDIR@@,$mandir,g;
s,@@LIBDEPENDS@@,$libdepends,g;
" < $program.pc.in >> $program.pc
chmod 400 $program.pc
fi
objs="`echo \"$sources\" | sed 's/\.c/\.o/g'`"
subst_vars "$program.pc"
subst_vars "$program.la"
subst_vars "rpm.spec"
echo "Creating config.h"
export_to_c $c_exports
echo "Creating config.mk"
export_to_make "$make_exports"
make clean >/dev/null 2>&1

View File

@ -33,7 +33,6 @@
#include <sys/queue.h>
#include <sys/types.h>
#include <stdint.h>
#include <sys/cdefs.h>
struct timespec;
@ -41,17 +40,15 @@ 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_USER (-11) /* User events */
#if !LIBKQUEUE
#define EVFILT_AIO (-3) /* attached to aio requests */
#define EVFILT_NETDEV (-8) /* network devices */
#define EVFILT_FS (-9) /* filesystem events */
#define EVFILT_LIO (-10) /* attached to lio requests */
#endif /* ! LIBKQUEUE */
#define EVFILT_USER (-11) /* User events */
#define EVFILT_SYSCOUNT 11
#define EV_SET(kevp_, a, b, c, d, e, f) do { \
@ -133,7 +130,6 @@ struct kevent {
* data/hint flags for EVFILT_PROC
*/
#define NOTE_EXIT 0x80000000 /* process exited */
#if ! LIBKQUEUE
#define NOTE_FORK 0x40000000 /* process forked */
#define NOTE_EXEC 0x20000000 /* process exec'd */
#define NOTE_PCTRLMASK 0xf0000000 /* mask for hint bits */
@ -143,23 +139,39 @@ struct kevent {
#define NOTE_TRACK 0x00000001 /* follow across forks */
#define NOTE_TRACKERR 0x00000002 /* could not track child */
#define NOTE_CHILD 0x00000004 /* am a child process */
#endif /* ! LIBKQUEUE */
#if ! LIBKQUEUE
/*
* 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 */
#endif /* ! LIBKQUEUE */
__BEGIN_DECLS
/* KLUDGE: This is from <sys/mount.h> on FreeBSD and is used by
the EVFILT_FS filter. */
/* vfsquery flags */
#define VQ_NOTRESP 0x0001 /* server down */
#define VQ_NEEDAUTH 0x0002 /* server bad auth */
#define VQ_LOWDISK 0x0004 /* we're low on space */
#define VQ_MOUNT 0x0008 /* new filesystem arrived */
#define VQ_UNMOUNT 0x0010 /* filesystem has left */
#define VQ_DEAD 0x0020 /* filesystem is dead, needs force unmount */
#define VQ_ASSIST 0x0040 /* filesystem needs assistance from external
program */
#define VQ_NOTRESPLOCK 0x0080 /* server lockd down */
#ifdef __cplusplus
extern "C" {
#endif
int kqueue(void);
int kevent(int kq, const struct kevent *changelist, int nchanges,
struct kevent *eventlist, int nevents,
const struct timespec *timeout);
__END_DECLS
#ifdef __cplusplus
}
#endif
#endif /* !_SYS_EVENT_H_ */

35
libkqueue.la.in Normal file
View File

@ -0,0 +1,35 @@
# @@PROGRAM@@.la - a libtool library file
# Generated by ltmain.sh - GNU libtool 1.5.20 (1.1220.2.287 2005/08/31 18:54:15)
#
# Please DO NOT delete this file!
# It is necessary for linking the library.
# The name that we can dlopen(3).
dlname='@@PROGRAM@@.so.0'
# Names of this library.
library_names='@@PROGRAM@@.so'
# The name of the static archive.
old_library='@@PROGRAM@@.a'
# Libraries that this one depends upon.
dependency_libs=' @@LIBDEPENDS@@'
# Version information for @@PROGRAM@@.
current=0
age=0
revision=0
# Is this an already installed library?
installed=yes
# Should we warn about portability when linking against -modules?
shouldnotlink=no
# Files to dlopen/dlpreopen
dlopen=''
dlpreopen=''
# Directory that this library needs to be installed in:
libdir='@@LIBDIR@@'

View File

@ -7,7 +7,7 @@ Name: @@PROGRAM@@
Description: Emulates FreeBSD kqueue(2) on other platforms
Version: @@VERSION@@
Requires:
Libs: -L${libdir} -lkqueue
Libs: @@LIBDEPENDS@@ -lkqueue
Libs.private: @@LIBDEPENDS@@
Cflags: -I${includedir}/kqueue

64
rpm.spec.in Normal file
View File

@ -0,0 +1,64 @@
#
# 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.
#
%define _topdir ./rpm
%define _rpmfilename %%{NAME}-%%{VERSION}-%%{RELEASE}.%%{ARCH}.rpm
Name: @@PROGRAM@@
Summary: @@PKG_SUMMARY@@
Version: @@VERSION@@
Release: 1
License: @@LICENSE@@
Vendor: @@AUTHOR@@
Group: System Environment/Libraries
Source0: %{name}-%version.tar.gz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-build
%description
@@PKG_DESCRIPTION@@
%prep
tar zxvf ../SOURCES/@@PROGRAM@@-@@VERSION@@.tar.gz
mv @@PROGRAM@@-@@VERSION@@/* .
%build
./configure --prefix=/usr
make
%install
make DESTDIR=%buildroot install
%clean
[ ${RPM_BUILD_ROOT} != "/" ] && rm -rf ${RPM_BUILD_ROOT}
%post
/sbin/ldconfig
%postun
/sbin/ldconfig
%files
%defattr(-,root,root)
/usr/include/kqueue/sys/event.h
/usr/lib/libkqueue.so
/usr/lib/libkqueue.la
/usr/lib/libkqueue.a
/usr/lib/pkgconfig/libkqueue.pc
/usr/share/man/man2/kqueue.2.gz
/usr/share/man/man2/kevent.2.gz
%changelog

View File

@ -46,7 +46,13 @@ filter_register(struct kqueue *kq, short filter, const struct filter *src)
dst = &kq->kq_filt[filt];
memcpy(dst, src, sizeof(*src));
dst->kf_kqueue = kq;
KNOTELIST_INIT(&dst->knl);
pthread_mutex_init(&dst->kf_mtx, NULL);
KNOTELIST_INIT(&dst->kf_watchlist);
KNOTELIST_INIT(&dst->kf_eventlist);
if (src->kf_id == 0) {
dbg_puts("filter is not implemented");
return (0);
}
if (src->kf_init == NULL) {
dbg_puts("filter has no initializer");
return (-1);
@ -60,15 +66,12 @@ filter_register(struct kqueue *kq, short filter, const struct filter *src)
}
/* 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);
if (dst->kf_pfd > 0) {
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);
}
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);
@ -111,7 +114,11 @@ filter_unregister_all(struct kqueue *kq)
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) {
for (n1 = LIST_FIRST(&kq->kq_filt[i].kf_watchlist); n1 != NULL; n1 = n2) {
n2 = LIST_NEXT(n1, entries);
free(n1);
}
for (n1 = LIST_FIRST(&kq->kq_filt[i].kf_eventlist); n1 != NULL; n1 = n2) {
n2 = LIST_NEXT(n1, entries);
free(n1);
}
@ -124,7 +131,7 @@ filter_socketpair(struct filter *filt)
{
int sockfd[2];
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd) < 0)
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfd) < 0)
return (-1);
fcntl(sockfd[0], F_SETFL, O_NONBLOCK);
@ -133,25 +140,29 @@ filter_socketpair(struct filter *filt)
return (0);
}
struct filter *
filter_lookup(struct kqueue *kq, short id)
int
filter_lookup(struct filter **filt, struct kqueue *kq, short id)
{
id = (-1 * id) - 1;
if (id < 0 || id >= EVFILT_SYSCOUNT) {
if (~id < 0 || ~id >= EVFILT_SYSCOUNT) {
dbg_printf("invalid id: id %d ~id %d", id, (~id));
errno = EINVAL;
return (NULL);
*filt = NULL;
return (-1);
}
if (kq->kq_filt[id].kf_copyin == NULL) {
errno = ENOTSUP;
return (NULL);
*filt = &kq->kq_filt[~id];
if ((*filt)->kf_copyin == NULL) {
errno = ENOSYS;
*filt = NULL;
return (-1);
}
return (&kq->kq_filt[id]);
return (0);
}
const char *
filter_name(short filt)
{
unsigned int id;
const char *fname[EVFILT_SYSCOUNT] = {
"EVFILT_READ",
"EVFILT_WRITE",
@ -166,8 +177,52 @@ filter_name(short filt)
"EVFILT_USER"
};
if (~filt >= EVFILT_SYSCOUNT)
return "EVFILT_BAD_RANGE";
id = ~filt;
if (id < 0 || id >= EVFILT_SYSCOUNT)
return "EVFILT_INVALID";
else
return fname[~filt];
return fname[id];
}
int
filter_raise(struct filter *filt)
{
for (;;) {
if (write(filt->kf_wfd, " ", 1) < 0) {
if (errno == EINTR)
continue;
if (errno != EAGAIN) {
dbg_printf("write(2): %s", strerror(errno));
/* TODO: set filter error flag */
return (-1);
}
}
break;
}
return (0);
}
int
filter_lower(struct filter *filt)
{
char buf[1024];
ssize_t n;
for (;;) {
n = read(filt->kf_pfd, &buf, sizeof(buf));
if (n < 0) {
if (errno == EINTR)
continue;
if (errno == EAGAIN)
break;
dbg_printf("read(2): %s", strerror(errno));
return (-1);
}
break;
}
return (0);
}

View File

@ -32,16 +32,99 @@
#include "sys/event.h"
#include "private.h"
static char *
kevent_filter_dump(const struct kevent *kev)
{
char *buf;
if ((buf = calloc(1, 64)) == NULL)
abort();
snprintf(buf, 64, "%d (%s)", kev->filter, filter_name(kev->filter));
return (buf);
}
static char *
kevent_fflags_dump(const struct kevent *kev)
{
char *buf;
#define KEVFFL_DUMP(attrib) \
if (kev->fflags & attrib) \
strncat(buf, #attrib" ", 64);
if ((buf = calloc(1, 1024)) == NULL)
abort();
snprintf(buf, 1024, "fflags=0x%04x (", kev->fflags);
if (kev->filter == EVFILT_VNODE) {
KEVFFL_DUMP(NOTE_DELETE);
KEVFFL_DUMP(NOTE_WRITE);
KEVFFL_DUMP(NOTE_EXTEND);
KEVFFL_DUMP(NOTE_ATTRIB);
KEVFFL_DUMP(NOTE_LINK);
KEVFFL_DUMP(NOTE_RENAME);
} else if (kev->filter == EVFILT_USER) {
KEVFFL_DUMP(NOTE_FFNOP);
KEVFFL_DUMP(NOTE_FFAND);
KEVFFL_DUMP(NOTE_FFOR);
KEVFFL_DUMP(NOTE_FFCOPY);
KEVFFL_DUMP(NOTE_TRIGGER);
} else {
strncat(buf, " ", 1);
}
buf[strlen(buf) - 1] = ')';
#undef KEVFFL_DUMP
return (buf);
}
static char *
kevent_flags_dump(const struct kevent *kev)
{
char *buf;
#define KEVFL_DUMP(attrib) \
if (kev->flags & attrib) \
strncat(buf, #attrib" ", 64);
if ((buf = calloc(1, 1024)) == NULL)
abort();
snprintf(buf, 1024, "flags=0x%04x (", kev->flags);
KEVFL_DUMP(EV_ADD);
KEVFL_DUMP(EV_ENABLE);
KEVFL_DUMP(EV_DISABLE);
KEVFL_DUMP(EV_DELETE);
KEVFL_DUMP(EV_ONESHOT);
KEVFL_DUMP(EV_CLEAR);
KEVFL_DUMP(EV_EOF);
KEVFL_DUMP(EV_ERROR);
KEVFL_DUMP(EV_DISPATCH);
KEVFL_DUMP(EV_RECEIPT);
buf[strlen(buf) - 1] = ')';
#undef KEVFL_DUMP
return (buf);
}
const char *
kevent_dump(struct kevent *kev)
kevent_dump(const struct kevent *kev)
{
char buf[512];
snprintf(&buf[0], sizeof(buf), "[filter=%d,flags=%d,ident=%u,udata=%p]",
kev->filter,
kev->flags,
snprintf(&buf[0], sizeof(buf),
"{ ident=%d, filter=%s, %s, %s, data=%d, udata=%p }",
(u_int) kev->ident,
kevent_filter_dump(kev),
kevent_flags_dump(kev),
kevent_fflags_dump(kev),
(int) kev->data,
kev->udata);
return (strdup(buf));
return (strdup(buf)); /* FIXME: memory leak */
}
static void
@ -51,21 +134,31 @@ kevent_error(struct kevent *dst, const struct kevent *src, int data)
dst->data = data;
}
/** @return number of events added to the eventlist */
static int
kevent_copyin(struct kqueue *kq, const struct kevent *src, int nchanges,
struct kevent *eventlist, int nevents)
{
struct knote *dst;
struct knote *dst = NULL;
struct filter *filt;
int kn_alloc,status;
int kn_alloc;
int status, rv;
kn_alloc = 0;
status = 0;
rv = 0;
dbg_printf("nchanges=%d nevents=%d", nchanges, nevents);
for (; nchanges > 0; src++, nchanges--) {
if ((filt = filter_lookup(kq, src->filter)) == NULL) {
status = -EINVAL;
goto err_out;
}
status = filter_lookup(&filt, kq, src->filter);
if (status < 0)
goto err_out_unlocked;
dbg_printf("%d %s\n", nchanges, kevent_dump(src));
filter_lock(filt);
/*
* Retrieve an existing knote object, or create a new one.
*/
@ -77,7 +170,6 @@ kevent_copyin(struct kqueue *kq, const struct kevent *src, int nchanges,
status = -ENOMEM;
goto err_out;
}
dbg_puts(kevent_dump(&dst->kev));
kn_alloc = 1;
} else if (src->flags & EV_ENABLE
|| src->flags & EV_DISABLE
@ -85,6 +177,14 @@ kevent_copyin(struct kqueue *kq, const struct kevent *src, int nchanges,
status = -ENOENT;
goto err_out;
} else {
/* Special case for EVFILT_USER:
Ignore user-generated events that are not of interest */
if (src->fflags & NOTE_TRIGGER) {
filter_unlock(filt);
continue;
}
/* flags == 0 or no action */
status = -EINVAL;
goto err_out;
@ -110,20 +210,24 @@ kevent_copyin(struct kqueue *kq, const struct kevent *src, int nchanges,
goto err_out;
}
filter_unlock(filt);
continue;
err_out:
filter_unlock(filt);
err_out_unlocked:
if (status != 0 && kn_alloc)
knote_free(dst);
if (nevents > 0) {
kevent_error(eventlist++, src, status);
nevents--;
rv++;
} else {
return (-1);
}
}
return (0);
return (rv);
}
int
@ -132,9 +236,7 @@ kevent(int kqfd, const struct kevent *changelist, int nchanges,
const struct timespec *timeout)
{
struct kqueue *kq;
struct filter *filt;
fd_set fds;
int i, rv, n, nret;
int rv, n, nret;
errno = 0;
@ -149,11 +251,13 @@ kevent(int kqfd, const struct kevent *changelist, int nchanges,
* 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);
if (rv > 0) {
eventlist += rv;
nevents -= rv;
}
}
/* Determine if we need to wait for events. */
@ -162,52 +266,24 @@ kevent(int kqfd, const struct kevent *changelist, int nchanges,
if (nevents > MAX_KEVENT)
nevents = MAX_KEVENT;
/*
* Wait for one or more filters to have events.
*/
wait_for_events:
fds = kq->kq_fds;
n = pselect(kq->kq_nfds, &fds, NULL , NULL, timeout, NULL);
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);
/* Handle spurious wakeups where no events are generated. */
if (nret == 0)
goto wait_for_events;
for (nret = 0; nret == 0;
nret = kevent_copyout(kq, n, eventlist, nevents))
{
/* Wait for one or more events. */
n = kevent_wait(kq, timeout);
if (n < 0)
return (-1);
if (n == 0)
return (0); /* Timeout */
}
#ifdef KQUEUE_DEBUG
dbg_printf("returning %d events", nret);
for (n = 0; n < nret; n++) {
dbg_printf("eventlist[%d] = %s", n, kevent_dump(&eventlist[n]));
}
#endif /* KQUEUE_DEBUG */
return (nret);
}

View File

@ -30,15 +30,15 @@ knote_new(struct filter *filt)
if ((dst = calloc(1, sizeof(*dst))) == NULL)
return (NULL);
KNOTE_INSERT(&filt->knl, dst);
KNOTE_INSERT(&filt->kf_watchlist, 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);
dbg_printf("filter=%s, ident=%u",
filter_name(kn->kev.filter), (u_int) kn->kev.ident);
LIST_REMOVE(kn, entries);
free(kn);
}
@ -48,20 +48,33 @@ struct knote *
knote_lookup(struct filter *filt, short ident)
{
struct knote *kn;
LIST_FOREACH(kn, &filt->knl, entries) {
/* TODO: Use rbtree for faster searching */
LIST_FOREACH(kn, &filt->kf_watchlist, entries) {
if (ident == kn->kev.ident)
break;
return (kn);
}
return (kn);
LIST_FOREACH(kn, &filt->kf_eventlist, entries) {
if (ident == kn->kev.ident)
return (kn);
}
return (NULL);
}
struct knote *
knote_lookup_data(struct filter *filt, intptr_t data)
{
struct knote *kn;
LIST_FOREACH(kn, &filt->knl, entries) {
LIST_FOREACH(kn, &filt->kf_watchlist, entries) {
if (data == kn->kev.data)
break;
return (kn);
}
return (kn);
LIST_FOREACH(kn, &filt->kf_eventlist, entries) {
if (data == kn->kev.data)
return (kn);
}
return (NULL);
}

View File

@ -30,258 +30,84 @@
#include "sys/event.h"
#include "private.h"
static int kqueue_initialized; /* Set to 1 by kqueue_init() */
static LIST_HEAD(,kqueue) kqlist = LIST_HEAD_INITIALIZER(&kqlist);
static size_t kqcount = 0;
static size_t kqcount;
#define kqlist_lock() pthread_mutex_lock(&kqlist_mtx)
#define kqlist_unlock() pthread_mutex_unlock(&kqlist_mtx)
static pthread_mutex_t kqlist_mtx = PTHREAD_MUTEX_INITIALIZER;
static sigset_t saved_sigmask;
static int kqgc_pfd[2];
static pthread_once_t kqueue_init_ctl = PTHREAD_ONCE_INIT;
static pthread_cond_t kqueue_init_cond = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t kqueue_init_mtx = PTHREAD_MUTEX_INITIALIZER;
static int kqueue_init_status = 0;
static void
mask_signals(void)
{
sigset_t mask;
sigemptyset (&mask);
if (pthread_sigmask(SIG_BLOCK, &mask, &saved_sigmask) != 0)
sigemptyset (&saved_sigmask);
}
static void
unmask_signals(void)
{
pthread_sigmask(SIG_SETMASK, &saved_sigmask, NULL);
}
static pthread_rwlock_t kqlist_mtx = PTHREAD_RWLOCK_INITIALIZER;
struct kqueue *
kqueue_lookup(int kq)
{
struct kqueue *ent = NULL;
kqlist_lock();
pthread_rwlock_rdlock(&kqlist_mtx);
LIST_FOREACH(ent, &kqlist, entries) {
if (ent->kq_sockfd[1] == kq)
break;
}
kqlist_unlock();
pthread_rwlock_unlock(&kqlist_mtx);
return (ent);
}
/* SEE ALSO: kqueue_shutdown */
static void
kqueue_destroy_unlocked(struct kqueue *kq)
/* Must hold the kqlist_mtx when calling this */
void
kqueue_free(struct kqueue *kq)
{
dbg_printf("fd=%d", kq->kq_sockfd[1]);
LIST_REMOVE(kq, entries);
filter_unregister_all(kq);
free(kq);
}
static void *
kqueue_close_wait(void *unused)
{
struct kqueue *kq;
char buf[1];
struct pollfd fds[MAX_KQUEUE + 1];
int wake_flag;
int i, n;
pthread_mutex_lock(&kqueue_init_mtx);
/* Block all signals in this thread */
sigset_t mask;
sigfillset(&mask);
sigprocmask(SIG_BLOCK, &mask, NULL);
wake_flag = 0;
pthread_cond_signal(&kqueue_init_cond);
pthread_mutex_unlock(&kqueue_init_mtx);
for (;;) {
/* This thread is woken up by writing to kqgc_pfd[1] */
fds[0].fd = kqgc_pfd[0];
fds[0].events = POLLIN;
n = 1;
kqlist_lock();
LIST_FOREACH(kq, &kqlist, entries) {
fds[n].fd = kq->kq_sockfd[0];
fds[n].events = POLLIN;
n++;
}
kqlist_unlock();
/* Inform the main thread that it can continue */
if (wake_flag) {
if (write (kqgc_pfd[0], ".", 1) < 1) {
/* TODO: error checking */
abort();
}
wake_flag = 0;
}
dbg_printf("gc: waiting for events on %d kqfds", n - 1);
n = poll(&fds[0], n, -1);
if (n == 0)
continue; /* Spurious wakeup */
if (n < 0) {
dbg_printf("poll(2): %s", strerror(errno));
abort(); //FIXME
}
dbg_printf("watcher: revents=%d", fds[0].revents);
if (fds[0].revents == POLLIN) {
dbg_puts("gc: waking up at user request");
if (read(kqgc_pfd[0], &buf, 1) < 0) {
abort();//TODO: err handling
}
wake_flag = 1;
} else {
/* Destroy any kqueue that close(2) has been called on */
kqlist_lock();
for (i = 1; i <= n; i++) {
dbg_printf("i=%d revents=%d", i, fds[n].revents);
if (fds[n].revents & POLLHUP) {
LIST_FOREACH(kq, &kqlist, entries) {
if (kq->kq_sockfd[0] == fds[n].fd) {
dbg_printf("gc: destroying kqueue; kqfd=%d",
kq->kq_sockfd[1]);
kqueue_destroy_unlocked(kq);
}
}
}
}
kqlist_unlock();
}
}
/* NOTREACHED */
return (NULL);
}
static void
kqgc_wakeup(void)
{
char buf[1];
ssize_t n;
dbg_puts("main: requesting wakeup");
n = write (kqgc_pfd[1], ".", 1);
if (n < 1) {
/* TODO: error checking */
abort();
}
/*
* There is a race condition as follows:
* main_thread GC_thread
* ----------- ---------
* kqueue() called
* create kqueue
* wake GC_thread ... (sleeping)
* kqueue() returns
* .. ..
* close(2) kqueue fd .
* wake and update pollset
*
* This would prevent the resources for the kqueue from
* ever being deallocated.
*
* To prevent this, the main_thread will wait for the
* gc_thread to update it's pollset before continuing.
*
*/
dbg_puts("main: waiting for gc to refresh it's pollset");
n = read(kqgc_pfd[1], &buf, 1);
if (n < 1) {
/* TODO: error checking */
abort();
}
dbg_puts("main: synchronization complete");
}
static void
static int
kqueue_init(void)
{
pthread_t tid;
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, kqgc_pfd) < 0)
goto errout;
if (kqueue_init_hook() < 0)
return (-1);
pthread_mutex_lock(&kqueue_init_mtx);
pthread_create(&tid, NULL, kqueue_close_wait, NULL);
pthread_cond_wait(&kqueue_init_cond, &kqueue_init_mtx);
pthread_mutex_unlock(&kqueue_init_mtx);
errout:
kqueue_init_status = -1;
}
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);
kqueue_initialized = 1;
return (0);
}
int
kqueue(void)
{
struct kqueue *kq;
int rv;
kq = calloc(1, sizeof(*kq));
if (kq == NULL)
return (-1);
pthread_mutex_init(&kq->kq_mtx, NULL);
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, kq->kq_sockfd) < 0)
if (socketpair(AF_UNIX, SOCK_STREAM, 0, kq->kq_sockfd) < 0)
goto errout_unlocked;
pthread_rwlock_wrlock(&kqlist_mtx);
if (!kqueue_initialized && kqueue_init() < 0)
goto errout;
(void) pthread_once(&kqueue_init_ctl, kqueue_init);
kqlist_lock();
mask_signals();
rv = filter_register_all(kq);
if (rv == 0) {
LIST_INSERT_HEAD(&kqlist, kq, entries);
kqcount++;
}
unmask_signals();
kqlist_unlock();
if (rv != 0)
if (filter_register_all(kq) < 0)
goto errout;
if (kqlist_insert_hook(kq) < 0)
goto errout;
if (kqueue_gc() < 0)
goto errout;
LIST_INSERT_HEAD(&kqlist, kq, entries);
kqcount++;
pthread_rwlock_unlock(&kqlist_mtx);
/* Force the GC thread to add the fd to it's pollset */
kqgc_wakeup();
dbg_printf("created kqueue: fd=%d", kq->kq_sockfd[1]);
dbg_printf("created kqueue, fd=%d", kq->kq_sockfd[1]);
return (kq->kq_sockfd[1]);
errout:
pthread_rwlock_unlock(&kqlist_mtx);
errout_unlocked:
//FIXME: unregister_all filters
if (kq->kq_sockfd[0] != kq->kq_sockfd[1]) {
// FIXME: close() may clobber errno in the case of EINTR
close(kq->kq_sockfd[0]);
close(kq->kq_sockfd[1]);
}

View File

@ -17,8 +17,15 @@
#ifndef _KQUEUE_PRIVATE_H
#define _KQUEUE_PRIVATE_H
#if defined (__SVR4) && defined (__sun)
#define SOLARIS
#include <port.h>
#endif
#include <errno.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <sys/select.h>
#include "sys/event.h"
@ -29,9 +36,9 @@
#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))
# define dbg_puts(str) fprintf(stderr, "%s(): %s\n", __func__,str)
# define dbg_printf(fmt,...) fprintf(stderr, "%s(): "fmt"\n", __func__,__VA_ARGS__)
# define dbg_perror(str) fprintf(stderr, "%s(): %s: %s\n", __func__,str, strerror(errno))
#else
# define dbg_puts(str) ;
# define dbg_printf(fmt,...) ;
@ -81,17 +88,22 @@ struct filter {
int kf_wfd; /* fd to write when an event occurs */
u_int kf_timeres; /* timer resolution, in miliseconds */
sigset_t kf_sigmask;
pthread_mutex_t kf_mtx;
struct evfilt_data *kf_data; /* filter-specific data */
struct knotelist knl;
struct knotelist kf_watchlist; /* events that have not occurred */
struct knotelist kf_eventlist; /* events that have occurred */
struct kqueue *kf_kqueue;
};
struct kqueue {
int kq_sockfd[2];
struct filter kq_filt[EVFILT_SYSCOUNT];
fd_set kq_fds;
fd_set kq_fds, kq_rfds;
int kq_nfds;
pthread_mutex_t kq_mtx;
#ifdef SOLARIS
int kq_port;
#endif
LIST_ENTRY(kqueue) entries;
};
@ -100,22 +112,27 @@ 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_lookup(struct filter **, 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 filter_lower(struct filter *);
int filter_raise(struct filter *);
#define filter_lock(f) pthread_mutex_lock(&(f)->kf_mtx)
#define filter_unlock(f) pthread_mutex_unlock(&(f)->kf_mtx)
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);
const char * kevent_dump(const struct kevent *);
int kevent_wait(struct kqueue *, const struct timespec *);
int kevent_copyout(struct kqueue *, int, struct kevent *, int);
void kevent_free(struct kqueue *);
struct kqueue * kqueue_lookup(int kq);
void kqueue_lock(struct kqueue *kq);
void kqueue_unlock(struct kqueue *kq);
/* TODO: make a kqops struct */
int kqueue_init_hook(void); // in hook.c
int kqlist_insert_hook(struct kqueue *); // in hook.c
int kqueue_gc(void); // in hook.c
void kqueue_free(struct kqueue *);
#endif /* ! _KQUEUE_PRIVATE_H */

128
src/linux/hook.c Normal file
View File

@ -0,0 +1,128 @@
/*
* 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 <sys/epoll.h>
#include <stdlib.h>
#include "sys/event.h"
#include "private.h"
static int gcfd;
static int
gcfd_ctl(int op, struct kqueue *kq)
{
struct epoll_event evt;
evt.data.ptr = kq;
evt.events = 0;
return (epoll_ctl(gcfd, op, kq->kq_sockfd[0], &evt));
}
int
kqueue_init_hook(void)
{
int epfd;
if ((epfd = epoll_create(1)) < 0) {
dbg_perror("epoll_create(2)");
return (-1);
}
gcfd = epfd;
return (0);
}
int
kqueue_gc(void)
{
struct epoll_event evt;
struct kqueue *kq;
int rv;
do {
rv = epoll_wait(gcfd, &evt, 1, 0);
if (rv > 0) {
kq = (struct kqueue *)evt.data.ptr;
rv = gcfd_ctl(EPOLL_CTL_DEL, kq);
kqueue_free(kq);
} else if (rv < 0) {
dbg_perror("epoll_wait(2)");
}
} while (rv > 0);
return (rv);
}
int
kqlist_insert_hook(struct kqueue *kq)
{
return (gcfd_ctl(EPOLL_CTL_ADD, kq));
}
int
kevent_wait(struct kqueue *kq, const struct timespec *timeout)
{
int n;
dbg_puts("waiting for events");
kq->kq_rfds = kq->kq_fds;
n = pselect(kq->kq_nfds, &kq->kq_rfds, NULL , NULL, timeout, NULL);
if (n < 0) {
if (errno == EINTR) {
dbg_puts("signal caught");
return (-1);
}
dbg_perror("pselect(2)");
return (-1);
}
return (n);
}
int
kevent_copyout(struct kqueue *kq, int nready,
struct kevent *eventlist, int nevents)
{
struct filter *filt;
int i, rv, nret;
nret = 0;
for (i = 0; (i < EVFILT_SYSCOUNT && nready > 0 && nevents > 0); i++) {
// dbg_printf("eventlist: n = %d nevents = %d", nready, nevents);
filt = &kq->kq_filt[i];
// dbg_printf("pfd[%d] = %d", i, filt->kf_pfd);
if (FD_ISSET(filt->kf_pfd, &kq->kq_rfds)) {
dbg_printf("pending events for %s",
filter_name(filt->kf_id));
filter_lock(filt);
rv = filt->kf_copyout(filt, eventlist, nevents);
if (rv < 0) {
filter_unlock(filt);
dbg_puts("kevent_copyout failed");
return (-1);
}
nret += rv;
eventlist += rv;
nevents -= rv;
nready--;
filter_unlock(filt);
}
}
return (nret);
}

View File

@ -35,20 +35,14 @@
#include "sys/event.h"
#include "private.h"
LIST_HEAD(proc_eventhead, proc_event);
struct proc_event {
uint64_t pe_kqid;
pid_t pe_pid;
int pe_status;
LIST_ENTRY(proc_event) entries;
};
/* XXX-FIXME Should only have one wait_thread per process.
Now, there is one thread per kqueue
*/
struct evfilt_data {
pthread_t wthr_id;
pthread_mutex_t wthr_mtx;
struct proc_eventhead wthr_waitq;
struct proc_eventhead wthr_eventq;
pthread_cond_t wait_cond;
pthread_mutex_t wait_mtx;
};
static void *
@ -57,7 +51,7 @@ wait_thread(void *arg)
struct filter *filt = (struct filter *) arg;
uint64_t counter = 1;
const int options = WEXITED | WNOWAIT;
struct proc_event *evt;
struct knote *kn;
siginfo_t si;
sigset_t sigmask;
@ -68,36 +62,48 @@ wait_thread(void *arg)
for (;;) {
/* Wait for a child process to exit(2) */
if (waitid(P_ALL, 0, &si, options) != 0)
break;
if (waitid(P_ALL, 0, &si, options) != 0) {
if (errno == ECHILD) {
dbg_puts("got ECHILD, waiting for wakeup condition");
pthread_mutex_lock(&filt->kf_data->wait_mtx);
pthread_cond_wait(&filt->kf_data->wait_cond, &filt->kf_data->wait_mtx);
pthread_mutex_unlock(&filt->kf_data->wait_mtx);
dbg_puts("awoken from ECHILD-induced sleep");
continue;
}
dbg_puts(" waitid(2) returned");
if (errno == EINTR)
continue;
dbg_perror("waitid(2)");
break;
}
/* Scan the wait queue to see if anyone is interested */
pthread_mutex_lock(&filt->kf_data->wthr_mtx);
LIST_FOREACH(evt, &filt->kf_data->wthr_waitq, entries) {
if (evt->pe_pid == si.si_pid)
break;
}
if (evt == NULL) {
pthread_mutex_unlock(&filt->kf_data->wthr_mtx);
pthread_mutex_lock(&filt->kf_mtx);
kn = knote_lookup(filt, si.si_pid);
if (kn == NULL) {
pthread_mutex_unlock(&filt->kf_mtx);
continue;
}
/* Create a proc_event */
if (si.si_code == CLD_EXITED) {
evt->pe_status = si.si_status;
kn->kev.data = si.si_status;
} else if (si.si_code == CLD_KILLED) {
/* FIXME: probably not true on BSD */
/* FIXME: arbitrary non-zero number */
evt->pe_status = 254;
kn->kev.data = 254;
} else {
/* Should never happen. */
evt->pe_status = 255;
/* FIXME: arbitrary non-zero number */
kn->kev.data = 1;
}
/* Add the event to the eventlist */
LIST_REMOVE(evt, entries);
LIST_INSERT_HEAD(&filt->kf_data->wthr_eventq, evt, entries);
pthread_mutex_unlock(&filt->kf_data->wthr_mtx);
/* Move the knote from the watchlist to the eventlist */
LIST_REMOVE(kn, entries);
LIST_INSERT_HEAD(&filt->kf_eventlist, kn, entries);
pthread_mutex_unlock(&filt->kf_mtx);
/* Indicate read(2) readiness */
if (write(filt->kf_pfd, &counter, sizeof(counter)) < 0) {
@ -114,14 +120,6 @@ wait_thread(void *arg)
return (NULL);
}
static int
watch_add(struct filter *filt, struct knote *kn)
{
pthread_mutex_lock(&filt->kf_data->wthr_mtx);
abort(); /*XXX-TODO*/
pthread_mutex_unlock(&filt->kf_data->wthr_mtx);
}
int
evfilt_proc_init(struct filter *filt)
{
@ -130,18 +128,18 @@ evfilt_proc_init(struct filter *filt)
if ((ed = calloc(1, sizeof(*ed))) == NULL)
return (-1);
pthread_mutex_init(&ed->wthr_mtx, NULL);
LIST_INIT(&ed->wthr_eventq);
LIST_INIT(&ed->wthr_waitq);
filt->kf_data = ed;
pthread_mutex_init(&ed->wait_mtx, NULL);
pthread_cond_init(&ed->wait_cond, NULL);
if ((efd = eventfd(0, 0)) < 0)
goto errout;
if (fcntl(filt->kf_pfd, F_SETFL, O_NONBLOCK) < 0)
goto errout;
filt->kf_pfd = efd;
if (pthread_create(&ed->wthr_id, NULL, wait_thread, filt) != 0)
goto errout;
filt->kf_pfd = efd;
return (0);
@ -166,7 +164,8 @@ evfilt_proc_copyin(struct filter *filt,
{
if (src->flags & EV_ADD && KNOTE_EMPTY(dst)) {
memcpy(&dst->kev, src, sizeof(*src));
watch_add(filt, dst);
/* TODO: Consider holding the mutex here.. */
pthread_cond_signal(&filt->kf_data->wait_cond);
}
if (src->flags & EV_ADD || src->flags & EV_ENABLE) {
@ -181,8 +180,7 @@ evfilt_proc_copyout(struct filter *filt,
struct kevent *dst,
int maxevents)
{
struct proc_event *elm;
struct knote *kn;
struct knote *kn, *kn_nxt;
int nevents = 0;
uint64_t cur;
@ -193,38 +191,32 @@ evfilt_proc_copyout(struct filter *filt,
}
dbg_printf(" counter=%llu", (unsigned long long) cur);
pthread_mutex_lock(&filt->kf_data->wthr_mtx);
LIST_FOREACH(elm, &filt->kf_data->wthr_eventq, entries) {
kn = knote_lookup(filt, elm->pe_pid);
if (kn == NULL) {
LIST_REMOVE(elm, entries);
free(elm);
continue;
}
pthread_mutex_lock(&filt->kf_mtx);
for (kn = LIST_FIRST(&filt->kf_eventlist); kn != NULL; kn = kn_nxt) {
kn_nxt = LIST_NEXT(kn, entries);
kevent_dump(&kn->kev);
dst->ident = kn->kev.ident;
dst->filter = kn->kev.filter;
dst->udata = kn->kev.udata;
dst->flags = kn->kev.flags;
dst->fflags = NOTE_EXIT;
dst->data = elm->pe_status;
memcpy(dst, &kn->kev, sizeof(*dst));
if (kn->kev.flags & EV_DISPATCH) {
KNOTE_DISABLE(kn);
}
if (kn->kev.flags & EV_ONESHOT)
if (kn->kev.flags & EV_ONESHOT) {
knote_free(kn);
} else {
kn->kev.data = 0;
LIST_REMOVE(kn, entries);
LIST_INSERT_HEAD(&filt->kf_watchlist, kn, entries);
}
LIST_REMOVE(elm, entries);
free(elm);
if (++nevents > maxevents)
break;
dst++;
}
pthread_mutex_unlock(&filt->kf_mtx);
if (!LIST_EMPTY(&filt->kf_data->wthr_eventq)) {
if (!LIST_EMPTY(&filt->kf_eventlist)) {
/* XXX-FIXME: If there are leftover events on the waitq,
re-arm the eventfd. list */
abort();
@ -234,7 +226,7 @@ evfilt_proc_copyout(struct filter *filt,
}
const struct filter evfilt_proc = {
EVFILT_PROC,
0, //XXX-FIXME broken: EVFILT_PROC,
evfilt_proc_init,
evfilt_proc_destroy,
evfilt_proc_copyin,

View File

@ -115,12 +115,8 @@ evfilt_signal_copyout(struct filter *filt,
continue;
dbg_printf("got signal %d", sig[i].ssi_signo);
memcpy(dst, &kn->kev, sizeof(*dst));
/* 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 = EV_ADD | EV_CLEAR;
dst->fflags = 0;
dst->data = 1;
if (kn->kev.flags & EV_DISPATCH || kn->kev.flags & EV_ONESHOT) {
@ -129,10 +125,8 @@ evfilt_signal_copyout(struct filter *filt,
}
if (kn->kev.flags & EV_DISPATCH)
KNOTE_DISABLE(kn);
if (kn->kev.flags & EV_ONESHOT) {
dst->flags |= EV_ONESHOT;
if (kn->kev.flags & EV_ONESHOT)
knote_free(kn);
}
dst++;
nevents++;

View File

@ -89,6 +89,7 @@ evfilt_socket_copyin(struct filter *filt,
op = EPOLL_CTL_ADD;
memcpy(&dst->kev, src, sizeof(*src));
}
memset(&ev, 0, sizeof(ev));
if (src->flags & EV_DELETE)
op = EPOLL_CTL_DEL;
if (src->flags & EV_ENABLE || src->flags & EV_DISABLE)
@ -175,15 +176,7 @@ evfilt_socket_copyout(struct filter *filt,
epoll_event_dump(ev);
kn = knote_lookup(filt, ev->data.fd);
if (kn != NULL) {
dst->ident = kn->kev.ident;
dst->filter = kn->kev.filter;
dst->udata = kn->kev.udata;
dst->flags = EV_ADD;
dst->fflags = 0;
if (kn->kev.flags & EV_ONESHOT) /*TODO: move elsewhere */
dst->flags |= EV_ONESHOT;
if (kn->kev.flags & EV_CLEAR) /*TODO: move elsewhere */
dst->flags |= EV_CLEAR;
memcpy(dst, &kn->kev, sizeof(*dst));
if (ev->events & EPOLLRDHUP || ev->events & EPOLLHUP)
dst->flags |= EV_EOF;
if (ev->events & EPOLLERR)

View File

@ -149,9 +149,10 @@ evfilt_timer_destroy(struct filter *filt)
struct knote *kn;
/* Destroy all timerfds */
KNOTELIST_FOREACH(kn, &filt->knl) {
KNOTELIST_FOREACH(kn, &filt->kf_watchlist) {
close(kn->kn_pfd);
}
/* TODO: use eventlist, close these also */
close (filt->kf_pfd);
}
@ -212,15 +213,7 @@ evfilt_timer_copyout(struct filter *filt,
ev = &epevt[i];
/* TODO: put in generic debug.c: epoll_event_dump(ev); */
kn = ev->data.ptr;
dst->ident = kn->kev.ident;
dst->filter = kn->kev.filter;
dst->udata = kn->kev.udata;
dst->flags = EV_ADD;
dst->fflags = 0;
if (kn->kev.flags & EV_ONESHOT) /*TODO: move elsewhere */
dst->flags |= EV_ONESHOT;
if (kn->kev.flags & EV_CLEAR) /*TODO: move elsewhere */
dst->flags |= EV_CLEAR;
memcpy(dst, &kn->kev, sizeof(*dst));
if (ev->events & EPOLLERR)
dst->fflags = 1; /* FIXME: Return the actual timer error */

View File

@ -30,6 +30,38 @@
#include "sys/event.h"
#include "private.h"
/* TODO: make common linux function */
static int
evfd_raise(int evfd)
{
uint64_t counter;
dbg_puts("efd_raise(): raising event level");
counter = 1;
if (write(evfd, &counter, sizeof(counter)) < 0) {
/* FIXME: handle EAGAIN */
dbg_printf("write(2): %s", strerror(errno));
return (-1);
}
return (0);
}
static int
evfd_lower(int evfd)
{
uint64_t cur;
/* Reset the counter */
dbg_puts("efd_lower(): lowering event level");
if (read(evfd, &cur, sizeof(cur)) < sizeof(cur)) {
/* FIXME: handle EAGAIN */
dbg_printf("read(2): %s", strerror(errno));
return (-1);
}
dbg_printf(" counter=%llu", (unsigned long long) cur);
return (0);
}
int
evfilt_user_init(struct filter *filt)
{
@ -54,11 +86,9 @@ evfilt_user_copyin(struct filter *filt,
{
u_int ffctrl;
struct kevent *kev;
uint64_t counter;
if (src->flags & EV_ADD && KNOTE_EMPTY(dst)) {
if (src->flags & EV_ADD && KNOTE_EMPTY(dst))
memcpy(&dst->kev, src, sizeof(*src));
}
kev = &dst->kev;
if (src->flags & EV_ENABLE) {
dst->kev.flags &= ~EV_DISABLE;
@ -97,15 +127,8 @@ evfilt_user_copyin(struct filter *filt,
}
if ((!(dst->kev.flags & EV_DISABLE)) && src->fflags & NOTE_TRIGGER) {
counter = 1;
if (write(filt->kf_pfd, &counter, sizeof(counter)) < 0) {
if (errno != EAGAIN) {
dbg_printf("write(2): %s", strerror(errno));
return (-1);
}
}
dst->kev.fflags |= NOTE_TRIGGER;
dbg_puts("knote triggered");
evfd_raise(filt->kf_pfd);
}
return (0);
@ -116,42 +139,44 @@ evfilt_user_copyout(struct filter *filt,
struct kevent *dst,
int maxevents)
{
struct knote *kn;
struct knote *kn, *kn_next;
int nevents = 0;
uint64_t cur;
for (kn = LIST_FIRST(&filt->kf_watchlist); kn != NULL; kn = kn_next) {
kn_next = LIST_NEXT(kn, entries);
/* Reset the counter */
if (read(filt->kf_pfd, &cur, sizeof(cur)) < sizeof(cur)) {
dbg_printf("read(2): %s", strerror(errno));
return (-1);
}
dbg_printf(" counter=%llu", (unsigned long long) cur);
/* Scan for events that have been triggered */
KNOTELIST_FOREACH(kn, &filt->knl) {
/* Skip knotes that have not been triggered */
if (!(kn->kev.fflags & NOTE_TRIGGER))
continue;
continue;
dst->ident = kn->kev.ident;
dst->filter = kn->kev.filter;
dst->udata = kn->kev.udata;
dst->flags = EV_ADD;
dst->fflags = kn->kev.fflags;
dst->data = 0;
if (kn->kev.flags & EV_ONESHOT) /*TODO: move elsewhere */
dst->flags |= EV_ONESHOT;
if (kn->kev.flags & EV_CLEAR) /*TODO: move elsewhere */
dst->flags |= EV_CLEAR;
memcpy(dst, &kn->kev, sizeof(*dst));
dst->fflags &= ~NOTE_FFCTRLMASK; //FIXME: Not sure if needed
dst->fflags &= ~NOTE_TRIGGER;
if (kn->kev.flags & EV_DISPATCH)
KNOTE_DISABLE(kn);
if (kn->kev.flags & EV_ADD) {
/* NOTE: True on FreeBSD but not consistent behavior with
other filters. */
dst->flags &= ~EV_ADD;
}
if (kn->kev.flags & EV_ONESHOT)
knote_free(kn);
if (kn->kev.flags & EV_CLEAR)
kn->kev.fflags &= ~NOTE_TRIGGER;
if (kn->kev.flags & (EV_DISPATCH | EV_CLEAR | EV_ONESHOT))
evfd_lower(filt->kf_pfd);
dst++;
if (++nevents == maxevents)
break;
}
/* This should normally never happen but is here for debugging */
if (nevents == 0) {
dbg_puts("spurious wakeup");
evfd_lower(filt->kf_pfd);
}
return (nevents);
}

View File

@ -229,12 +229,8 @@ evfilt_vnode_copyout(struct filter *filt,
return (-1);
}
kevent_dump(&kn->kev);
dst->ident = kn->kev.ident;
dst->filter = kn->kev.filter;
dst->udata = kn->kev.udata;
dst->flags = kn->kev.flags;
dst->fflags = 0;
memcpy(dst, &kn->kev, sizeof(*dst));
dst->data = 0;
/* No error checking because fstat(2) should rarely fail */
if ((evt.mask & IN_ATTRIB || evt.mask & IN_MODIFY)

190
src/posix/proc.c Normal file
View File

@ -0,0 +1,190 @@
/*
* 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 <err.h>
#include <fcntl.h>
#include <pthread.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/queue.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <unistd.h>
#include <limits.h>
#include "sys/event.h"
#include "private.h"
pthread_cond_t wait_cond = PTHREAD_COND_INITIALIZER;
pthread_mutex_t wait_mtx = PTHREAD_MUTEX_INITIALIZER;
struct evfilt_data {
pthread_t wthr_id;
};
static void *
wait_thread(void *arg)
{
struct filter *filt = (struct filter *) arg;
struct knote *kn;
int status, result;
pid_t pid;
sigset_t sigmask;
/* Block all signals */
sigfillset (&sigmask);
sigdelset(&sigmask, SIGCHLD);
pthread_sigmask(SIG_BLOCK, &sigmask, NULL);
for (;;) {
/* Wait for a child process to exit(2) */
if ((pid = waitpid(-1, &status, 0)) < 0) {
if (errno == ECHILD) {
dbg_puts("got ECHILD, waiting for wakeup condition");
pthread_mutex_lock(&wait_mtx);
pthread_cond_wait(&wait_cond, &wait_mtx);
pthread_mutex_unlock(&wait_mtx);
dbg_puts("awoken from ECHILD-induced sleep");
continue;
}
if (errno == EINTR)
continue;
dbg_printf("wait(2): %s", strerror(errno));
break;
}
/* Create a proc_event */
if (WIFEXITED(status)) {
result = WEXITSTATUS(status);
} else if (WIFSIGNALED(status)) {
/* FIXME: probably not true on BSD */
result = WTERMSIG(status);
} else {
dbg_puts("unexpected code path");
result = 234; /* arbitrary error value */
}
/* Scan the wait queue to see if anyone is interested */
pthread_mutex_lock(&filt->kf_mtx);
kn = knote_lookup(filt, pid);
if (kn != NULL) {
kn->kev.data = result;
kn->kev.fflags = NOTE_EXIT;
LIST_REMOVE(kn, entries);
LIST_INSERT_HEAD(&filt->kf_eventlist, kn, entries);
/* Indicate read(2) readiness */
/* TODO: error handling */
filter_raise(filt);
}
pthread_mutex_unlock(&filt->kf_mtx);
}
/* TODO: error handling */
return (NULL);
}
int
evfilt_proc_init(struct filter *filt)
{
struct evfilt_data *ed;
if ((ed = calloc(1, sizeof(*ed))) == NULL)
return (-1);
if (filter_socketpair(filt) < 0)
goto errout;
if (pthread_create(&ed->wthr_id, NULL, wait_thread, filt) != 0)
goto errout;
return (0);
errout:
free(ed);
return (-1);
}
void
evfilt_proc_destroy(struct filter *filt)
{
//TODO: pthread_cancel(filt->kf_data->wthr_id);
close(filt->kf_pfd);
}
int
evfilt_proc_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));
/* TODO: think about locking the mutex first.. */
pthread_cond_signal(&wait_cond);
}
if (src->flags & EV_ADD || src->flags & EV_ENABLE) {
/* Nothing to do.. */
}
return (0);
}
int
evfilt_proc_copyout(struct filter *filt,
struct kevent *dst,
int maxevents)
{
struct knote *kn;
int nevents = 0;
filter_lower(filt);
LIST_FOREACH(kn, &filt->kf_eventlist, entries) {
kevent_dump(&kn->kev);
memcpy(dst, &kn->kev, sizeof(*dst));
dst->fflags = NOTE_EXIT;
if (kn->kev.flags & EV_DISPATCH) {
KNOTE_DISABLE(kn);
}
#if FIXME
/* XXX - NEED TO use safe foreach instead */
if (kn->kev.flags & EV_ONESHOT)
knote_free(kn);
#endif
if (++nevents > maxevents)
break;
dst++;
}
if (!LIST_EMPTY(&filt->kf_eventlist))
filter_raise(filt);
return (nevents);
}
const struct filter evfilt_proc = {
EVFILT_PROC,
evfilt_proc_init,
evfilt_proc_destroy,
evfilt_proc_copyin,
evfilt_proc_copyout,
};

97
src/solaris/hook.c Normal file
View File

@ -0,0 +1,97 @@
/*
* 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 <string.h>
#include "sys/event.h"
#include "private.h"
#define PORTEV_DUMP(pe) dbg_printf("port_event: events=%d source=%hu", \
(pe)->portev_events, (pe)->portev_source)
int
kqueue_init_hook(void)
{
return (0);
}
int
kqueue_hook(struct kqueue *kq)
{
if ((kq->kq_port = port_create()) < 0) {
dbg_perror("port_create(2)");
return (-1);
}
return (0);
}
int
kevent_wait(struct kqueue *kq, const struct timespec *timeout)
{
port_event_t pe[1];
int rv, nget;
dbg_printf("port_get: %d %lu %lu",
kq->kq_port, timeout->tv_sec, timeout->tv_nsec);
nget = 1;
rv = port_getn(kq->kq_port, pe, 1, &nget, (struct timespec *) timeout);
if (rv < 0) {
if (errno == EINTR) {
dbg_puts("signal caught");
return (-1);
}
dbg_perror("port_getn(2)");
return (-1);
}
if (nget > 0) {
dbg_printf(" -- event(s) pending: nget = %d", nget);
PORTEV_DUMP(&pe[0]);
} else {
dbg_puts(" -- no events --");
}
return (nget);
}
int
kevent_copyout(struct kqueue *kq, int nready,
struct kevent *eventlist, int nevents)
{
return (-1);
#if TODO
struct filter *filt;
int i, rv, nret;
if (FD_ISSET(filt->kf_pfd, &kq->kq_rfds)) {
dbg_printf("event(s) for filter #%d", i);
filter_lock(filt);
rv = filt->kf_copyout(filt, eventlist, nevents);
if (rv < 0) {
filter_unlock(filt);
dbg_puts("kevent_copyout failed");
return (-1);
}
nret += rv;
eventlist += rv;
nevents -= rv;
nready--;
filter_unlock(filt);
}
return (nret);
#endif
}

111
src/solaris/proc.c Normal file
View File

@ -0,0 +1,111 @@
/*
* 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 "sys/event.h"
#include "private.h"
int
evfilt_proc_init(struct filter *filt)
{
return (-1);
}
void
evfilt_proc_destroy(struct filter *filt)
{
;
}
int
evfilt_proc_copyin(struct filter *filt,
struct knote *dst, const struct kevent *src)
{
return (-1);
#if TODO
if (src->flags & EV_ADD && KNOTE_EMPTY(dst)) {
memcpy(&dst->kev, src, sizeof(*src));
/* TODO: Consider holding the mutex here.. */
pthread_cond_signal(&filt->kf_data->wait_cond);
}
if (src->flags & EV_ADD || src->flags & EV_ENABLE) {
/* Nothing to do.. */
}
return (0);
#endif
}
int
evfilt_proc_copyout(struct filter *filt,
struct kevent *dst,
int maxevents)
{
return (-1);
#if TODO
struct knote *kn, *kn_nxt;
int nevents = 0;
uint64_t cur;
/* Reset the counter */
if (read(filt->kf_pfd, &cur, sizeof(cur)) < sizeof(cur)) {
dbg_printf("read(2): %s", strerror(errno));
return (-1);
}
dbg_printf(" counter=%llu", (unsigned long long) cur);
pthread_mutex_lock(&filt->kf_mtx);
for (kn = LIST_FIRST(&filt->kf_eventlist); kn != NULL; kn = kn_nxt) {
kn_nxt = LIST_NEXT(kn, entries);
kevent_dump(&kn->kev);
memcpy(dst, &kn->kev, sizeof(*dst));
if (kn->kev.flags & EV_DISPATCH) {
KNOTE_DISABLE(kn);
}
if (kn->kev.flags & EV_ONESHOT) {
knote_free(kn);
} else {
kn->kev.data = 0;
LIST_REMOVE(kn, entries);
LIST_INSERT_HEAD(&filt->kf_watchlist, kn, entries);
}
if (++nevents > maxevents)
break;
dst++;
}
pthread_mutex_unlock(&filt->kf_mtx);
if (!LIST_EMPTY(&filt->kf_eventlist)) {
/* XXX-FIXME: If there are leftover events on the waitq,
re-arm the eventfd. list */
abort();
}
return (nevents);
#endif
}
const struct filter evfilt_proc = {
0, //EVFILT_PROC,
evfilt_proc_init,
evfilt_proc_destroy,
evfilt_proc_copyin,
evfilt_proc_copyout,
};

117
src/solaris/signal.c Normal file
View File

@ -0,0 +1,117 @@
/*
* 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 "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 (-1);
}
void
evfilt_signal_destroy(struct filter *filt)
{
;
}
int
evfilt_signal_copyin(struct filter *filt,
struct knote *dst, const struct kevent *src)
{
if (src->ident >= SIGNAL_MAX) {
dbg_printf("unsupported signal number %u", (u_int) src->ident);
return (-1);
}
return (-1);
#if TODO
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);
return (update_sigmask(filt));
#endif
}
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;
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);
memcpy(dst, &kn->kev, sizeof(*dst));
/* TODO: dst->data should be the number of times the signal occurred */
dst->data = 1;
if (kn->kev.flags & EV_DISPATCH || kn->kev.flags & EV_ONESHOT) {
sigdelset(&filt->kf_sigmask, dst->ident);
update_sigmask(filt); /* TODO: error checking */
}
if (kn->kev.flags & EV_DISPATCH)
KNOTE_DISABLE(kn);
if (kn->kev.flags & EV_ONESHOT)
knote_free(kn);
dst++;
nevents++;
}
return (nevents);
#endif
}
const struct filter evfilt_signal = {
0, //EVFILT_SIGNAL,
evfilt_signal_init,
evfilt_signal_destroy,
evfilt_signal_copyin,
evfilt_signal_copyout,
};

110
src/solaris/socket.c Normal file
View File

@ -0,0 +1,110 @@
/*
* 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 <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 <port.h>
#include "sys/event.h"
#include "private.h"
int
evfilt_socket_init(struct filter *filt)
{
return (0);
}
void
evfilt_socket_destroy(struct filter *filt)
{
;
}
int
evfilt_socket_copyin(struct filter *filt,
struct knote *dst, const struct kevent *src)
{
int port, events;
int rv;
port = filt->kf_kqueue->kq_port;
/* Not supported or not implemented */
if (src->flags & EV_CLEAR) {
dbg_puts("attempt to use unsupported mechanism");
return (-1);
}
if (src->filter == EVFILT_READ)
events = POLLIN;
else
events = POLLOUT;
if (src->flags & EV_ADD && KNOTE_EMPTY(dst))
memcpy(&dst->kev, src, sizeof(*src));
if (src->flags & EV_DELETE || src->flags & EV_DISABLE) {
rv = port_dissociate(port, PORT_SOURCE_FD, src->ident);
if (rv < 0) {
dbg_perror("port_disassociate(2)");
return (-1);
}
}
if (src->flags & EV_ENABLE || src->flags & EV_ADD) {
rv = port_associate(port, PORT_SOURCE_FD,
src->ident, events, src->udata);
if (rv < 0) {
dbg_perror("port_associate(2)");
return (-1);
}
}
/* XXX-TODO support modifying an existing watch */
return (0);
}
int
evfilt_socket_copyout(struct filter *filt,
struct kevent *dst,
int nevents)
{
return (-1);
}
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,
};

125
src/solaris/timer.c Normal file
View File

@ -0,0 +1,125 @@
/*
* 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 <port.h>
#include "sys/event.h"
#include "private.h"
int
evfilt_timer_init(struct filter *filt)
{
return (-1);
}
void
evfilt_timer_destroy(struct filter *filt)
{
;
}
int
evfilt_timer_copyin(struct filter *filt,
struct knote *dst, const struct kevent *src)
{
return (-1);
#if TODO
if (src->flags & EV_ADD && KNOTE_EMPTY(dst)) {
memcpy(&dst->kev, src, sizeof(*src));
dst->kev.flags |= EV_CLEAR;
}
if (src->flags & EV_ADD)
return ktimer_create(filt, dst);
if (src->flags & EV_DELETE)
return ktimer_delete(filt, dst);
if (src->flags & EV_ENABLE)
return ktimer_create(filt, dst);
if (src->flags & EV_DISABLE) {
// TODO: err checking
(void) ktimer_delete(filt, dst);
KNOTE_DISABLE(dst);
}
return (0);
#endif
}
int
evfilt_timer_copyout(struct filter *filt,
struct kevent *dst,
int nevents)
{
return (-1);
#if TODO
struct epoll_event epevt[MAX_KEVENT];
struct epoll_event *ev;
struct knote *kn;
uint64_t expired;
int i, nret;
ssize_t n;
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++) {
ev = &epevt[i];
/* TODO: put in generic debug.c: epoll_event_dump(ev); */
kn = ev->data.ptr;
memcpy(dst, &kn->kev, sizeof(*dst));
if (ev->events & EPOLLERR)
dst->fflags = 1; /* FIXME: Return the actual timer error */
/* On return, data contains the number of times the
timer has been trigered.
*/
n = read(kn->kn_pfd, &expired, sizeof(expired));
if (n < 0 || n < sizeof(expired)) {
dbg_puts("invalid read from timerfd");
expired = 1; /* Fail gracefully */
}
dst->data = expired;
if (kn->kev.flags & EV_DISPATCH)
KNOTE_DISABLE(kn);
if (kn->kev.flags & EV_ONESHOT) {
ktimer_delete(filt, kn);
knote_free(kn);
}
nevents++;
dst++;
}
return (nevents);
#endif
}
const struct filter evfilt_timer = {
0, //EVFILT_TIMER,
evfilt_timer_init,
evfilt_timer_destroy,
evfilt_timer_copyin,
evfilt_timer_copyout,
};

151
src/solaris/user.c Normal file
View File

@ -0,0 +1,151 @@
/*
* 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 "sys/event.h"
#include "private.h"
int
evfilt_user_init(struct filter *filt)
{
return (-1);
}
void
evfilt_user_destroy(struct filter *filt)
{
return;
}
int
evfilt_user_copyin(struct filter *filt,
struct knote *dst, const struct kevent *src)
{
return (-1);
#if TODO
u_int ffctrl;
struct kevent *kev;
uint64_t counter;
if (src->flags & EV_ADD && KNOTE_EMPTY(dst))
memcpy(&dst->kev, src, sizeof(*src));
kev = &dst->kev;
if (src->flags & EV_ENABLE) {
dst->kev.flags &= ~EV_DISABLE;
/* FIXME: what happens if NOTE_TRIGGER is in fflags?
should the event fire? */
}
if (src->flags & EV_DISABLE)
dst->kev.flags |= EV_DISABLE;
/* FIXME: can oneshot be added after the knote is already created? */
if (src->flags & EV_ONESHOT)
dst->kev.flags |= EV_ONESHOT;
/* Excerpted from sys/kern/kern_event.c in FreeBSD HEAD */
ffctrl = kev->fflags & NOTE_FFCTRLMASK;
kev->fflags &= NOTE_FFLAGSMASK;
switch (ffctrl) {
case NOTE_FFNOP:
break;
case NOTE_FFAND:
kev->fflags &= src->fflags;
break;
case NOTE_FFOR:
kev->fflags |= src->fflags;
break;
case NOTE_FFCOPY:
kev->fflags = kev->fflags;
break;
default:
/* XXX Return error? */
break;
}
if ((!(dst->kev.flags & EV_DISABLE)) && src->fflags & NOTE_TRIGGER) {
counter = 1;
if (write(filt->kf_pfd, &counter, sizeof(counter)) < 0) {
if (errno != EAGAIN) {
dbg_printf("write(2): %s", strerror(errno));
return (-1);
}
}
dst->kev.fflags |= NOTE_TRIGGER;
dbg_puts("knote triggered");
}
#endif
return (0);
}
int
evfilt_user_copyout(struct filter *filt,
struct kevent *dst,
int maxevents)
{
return (-1);
#if TODO
struct knote *kn, *kn_next;
int nevents = 0;
uint64_t cur;
/* Reset the counter */
if (read(filt->kf_pfd, &cur, sizeof(cur)) < sizeof(cur)) {
dbg_printf("read(2): %s", strerror(errno));
return (-1);
}
dbg_printf(" counter=%llu", (unsigned long long) cur);
for (kn = LIST_FIRST(&filt->kf_watchlist); kn != NULL; kn = kn_next) {
kn_next = LIST_NEXT(kn, entries);
memcpy(dst, &kn->kev, sizeof(*dst));
dst->fflags &= ~NOTE_FFCTRLMASK; //FIXME: Not sure if needed
dst->fflags &= ~NOTE_TRIGGER;
if (kn->kev.flags & EV_DISPATCH)
KNOTE_DISABLE(kn);
if (kn->kev.flags & EV_ADD) {
/* NOTE: True on FreeBSD but not consistent behavior with
other filters. */
dst->flags &= ~EV_ADD;
}
if (kn->kev.flags & EV_ONESHOT) {
/* NOTE: True on FreeBSD but not consistent behavior with
other filters. */
dst->flags &= ~EV_ONESHOT;
knote_free(kn);
}
dst++;
if (++nevents == maxevents)
break;
}
return (nevents);
#endif
}
const struct filter evfilt_user = {
0, //EVFILT_USER,
evfilt_user_init,
evfilt_user_destroy,
evfilt_user_copyin,
evfilt_user_copyout,
};

172
src/solaris/vnode.c Normal file
View 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 "sys/event.h"
#include "private.h"
int
evfilt_vnode_init(struct filter *filt)
{
return (-1);
}
void
evfilt_vnode_destroy(struct filter *filt)
{
;
}
int
evfilt_vnode_copyin(struct filter *filt,
struct knote *dst, const struct kevent *src)
{
#if TODO
char path[PATH_MAX];
struct stat sb;
uint32_t mask;
if (src->flags & EV_DELETE || src->flags & EV_DISABLE)
return delete_watch(filt->kf_pfd, dst);
if (src->flags & EV_ADD && KNOTE_EMPTY(dst)) {
memcpy(&dst->kev, src, sizeof(*src));
if (fstat(src->ident, &sb) < 0) {
dbg_puts("fstat failed");
return (-1);
}
dst->kn_st_nlink = sb.st_nlink;
dst->kn_st_size = sb.st_size;
dst->kev.data = -1;
}
if (src->flags & EV_ADD || src->flags & EV_ENABLE) {
/* Convert the fd to a pathname */
if (fd_to_path(&path[0], sizeof(path), src->ident) < 0)
return (-1);
/* Convert the fflags to the inotify mask */
mask = 0;
if (dst->kev.fflags & NOTE_DELETE)
mask |= IN_ATTRIB | IN_DELETE_SELF;
if (dst->kev.fflags & NOTE_WRITE)
mask |= IN_MODIFY | IN_ATTRIB;
if (dst->kev.fflags & NOTE_EXTEND)
mask |= IN_MODIFY | IN_ATTRIB;
if ((dst->kev.fflags & NOTE_ATTRIB) ||
(dst->kev.fflags & NOTE_LINK))
mask |= IN_ATTRIB;
if (dst->kev.fflags & NOTE_RENAME)
mask |= IN_MOVE_SELF;
if (dst->kev.flags & EV_ONESHOT)
mask |= IN_ONESHOT;
dbg_printf("inotify_add_watch(2); inofd=%d, %s, path=%s",
filt->kf_pfd, inotify_mask_dump(mask), path);
dst->kev.data = inotify_add_watch(filt->kf_pfd, path, mask);
if (dst->kev.data < 0) {
dbg_printf("inotify_add_watch(2): %s", strerror(errno));
return (-1);
}
}
return (0);
#endif
return (-1);
}
int
evfilt_vnode_copyout(struct filter *filt,
struct kevent *dst,
int nevents)
{
#if TODO
struct inotify_event evt;
struct stat sb;
struct knote *kn;
if (get_one_event(&evt, filt->kf_pfd) < 0)
return (-1);
inotify_event_dump(&evt);
if (evt.mask & IN_IGNORED) {
/* TODO: possibly return error when fs is unmounted */
return (0);
}
kn = knote_lookup_data(filt, evt.wd);
if (kn == NULL) {
dbg_printf("no match for wd # %d", evt.wd);
return (-1);
}
memcpy(dst, &kn->kev, sizeof(*dst));
dst->data = 0;
/* No error checking because fstat(2) should rarely fail */
if ((evt.mask & IN_ATTRIB || evt.mask & IN_MODIFY)
&& fstat(kn->kev.ident, &sb) == 0) {
if (sb.st_nlink == 0 && kn->kev.fflags & NOTE_DELETE)
dst->fflags |= NOTE_DELETE;
if (sb.st_nlink != kn->kn_st_nlink && kn->kev.fflags & NOTE_LINK)
dst->fflags |= NOTE_LINK;
#if HAVE_NOTE_TRUNCATE
if (sb.st_nsize == 0 && kn->kev.fflags & NOTE_TRUNCATE)
dst->fflags |= NOTE_TRUNCATE;
#endif
if (sb.st_size > kn->kn_st_size && kn->kev.fflags & NOTE_WRITE)
dst->fflags |= NOTE_EXTEND;
kn->kn_st_nlink = sb.st_nlink;
kn->kn_st_size = sb.st_size;
}
if (evt.mask & IN_MODIFY && kn->kev.fflags & NOTE_WRITE)
dst->fflags |= NOTE_WRITE;
if (evt.mask & IN_ATTRIB && kn->kev.fflags & NOTE_ATTRIB)
dst->fflags |= NOTE_ATTRIB;
if (evt.mask & IN_MOVE_SELF && kn->kev.fflags & NOTE_RENAME)
dst->fflags |= NOTE_RENAME;
if (evt.mask & IN_DELETE_SELF && kn->kev.fflags & NOTE_DELETE)
dst->fflags |= NOTE_DELETE;
if (evt.mask & IN_MODIFY && kn->kev.fflags & NOTE_WRITE)
dst->fflags |= NOTE_WRITE;
if (evt.mask & IN_ATTRIB && kn->kev.fflags & NOTE_ATTRIB)
dst->fflags |= NOTE_ATTRIB;
if (evt.mask & IN_MOVE_SELF && kn->kev.fflags & NOTE_RENAME)
dst->fflags |= NOTE_RENAME;
if (evt.mask & IN_DELETE_SELF && kn->kev.fflags & NOTE_DELETE)
dst->fflags |= NOTE_DELETE;
if (kn->kev.flags & EV_DISPATCH) {
delete_watch(filt->kf_pfd, kn); /* TODO: error checking */
KNOTE_DISABLE(kn);
}
if (kn->kev.flags & EV_ONESHOT)
knote_free(kn);
return (1);
#endif
return (-1);
}
const struct filter evfilt_vnode = {
0, //EVFILT_VNODE,
evfilt_vnode_init,
evfilt_vnode_destroy,
evfilt_vnode_copyin,
evfilt_vnode_copyout,
};

View File

@ -16,27 +16,31 @@
include config.mk
check:
if [ ! -f /usr/include/sys/event.h ] ; then \
cd .. && ./configure --debug=yes && make build ; \
fi
$(CC) $(CFLAGS) $(SOURCES) $(LDADD)
./a.out
all: kqtest
kqtest: $(SOURCES)
test -L libkqueue.a || ln -s ../libkqueue.a
$(CC) -o kqtest $(CFLAGS) $(SOURCES) $(LDADD)
check: kqtest
./kqtest
# NOTE: copy+paste of 'make check'
valgrind:
cd .. && make build CFLAGS="$(CFLAGS) -g -O0 -DKQUEUE_DEBUG -DUNIT_TEST"
gcc -c $(CFLAGS) test.c
gcc -g -O0 $(CFLAGS) test.c libkqueue.a $(LDADD)
valgrind --tool=memcheck --leak-check=full --show-reachable=yes --num-callers=20 --track-fds=yes ./a.out
valgrind: kqtest
valgrind --tool=memcheck --leak-check=full --show-reachable=yes --num-callers=20 --track-fds=yes ./kqtest
check-installed:
gcc $(CFLAGS) -I$(PREFIX)/kqueue test.c -lkqueue
./a.out
$(CC) -o kqtest $(CFLAGS) $(SOURCES) $(LDADD) -lkqueue
./kqtest
distclean: clean
rm -f config.mk config.h a.out
check-libtool:
gcc $(CFLAGS) -c *.c
libtool --mode=link gcc -g -O0 -o kqtest *.o $(LIBDIR)/libkqueue.la
./kqtest
distclean: clean
rm -f config.mk config.h
for x in libdispatch ; do cd $$x && make distclean ; done
clean:
rm -f *.o
rm -f *.o *.a kqtest

View File

@ -18,7 +18,12 @@
#define _COMMON_H
#include <err.h>
#if HAVE_ERR_H
# include <err.h>
#else
# define err(rc,msg,...) do { perror(msg); exit(rc); } while (0)
# define errx(rc,msg,...) do { puts(msg); exit(rc); } while (0)
#endif
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
@ -43,6 +48,15 @@ struct kevent * kevent_get(int);
void kevent_cmp(struct kevent *, struct kevent *);
void
kevent_add(int kqfd, struct kevent *kev,
uintptr_t ident,
short filter,
u_short flags,
u_int fflags,
intptr_t data,
void *udata);
/* DEPRECATED: */
#define KEV_CMP(kev,_ident,_filter,_flags) do { \
if (kev.ident != (_ident) || \
@ -57,6 +71,6 @@ void kevent_cmp(struct kevent *, struct kevent *);
extern void test_no_kevents(void);
extern void test_begin(const char *);
extern void success(const char *);
extern void success(void);
#endif /* _COMMON_H */

View File

@ -7,14 +7,17 @@ sources="main.c proc.c read.c signal.c timer.c vnode.c"
pre_configure_hook() {
check_header "err.h"
check_header "sys/event.h" \
&& sys_event_h="sys/event.h" \
|| {
sys_event_h="../include/sys/event.h"
cflags="$cflags -I../include"
ldadd="$ldadd ../libkqueue.a -lpthread -lrt"
ldadd="$ldadd libkqueue.a -lpthread -lrt"
}
test "$target" = "solaris" && ldadd="$ldadd -lsocket"
check_symbol $sys_event_h EV_DISPATCH
check_symbol $sys_event_h EV_RECEIPT
check_symbol $sys_event_h NOTE_TRUNCATE

16
test/libdispatch/Makefile Normal file
View File

@ -0,0 +1,16 @@
CFLAGS=`pkg-config --cflags libkqueue`
LDADD=`pkg-config --libs libkqueue` -ldispatch
all: disptest
disptest: main.o
$(CC) -o disptest $(CFLAGS) main.c $(LDADD)
check: disptest
./disptest
clean:
rm -f *.o
distclean: clean
rm -f disptest

99
test/libdispatch/main.c Normal file
View File

@ -0,0 +1,99 @@
/*
* 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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/event.h>
#include <dispatch/dispatch.h>
pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
int testnum;
void test_countdown(void);
void
say_hello(void *arg)
{
puts("hello");
test_countdown();
}
void
final_countdown(void *arg, size_t count)
{
static int europe = 10;
if (europe == 0) {
printf("It's the final countdown..\n");
exit(0);
} else {
printf("%d.. ", europe);
fflush(stdout);
}
pthread_mutex_lock(&mtx);
europe--;
pthread_mutex_unlock(&mtx);
}
/* Adapted from:
http://developer.apple.com/mac/articles/cocoa/introblocksgcd.html
*/
void
test_timer()
{
dispatch_source_t timer;
dispatch_time_t now;
timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0,
dispatch_get_current_queue()); //NOTE: q_default doesn't work
now = dispatch_walltime(DISPATCH_TIME_NOW, 0);
dispatch_source_set_timer(timer, now, 1, 1);
dispatch_source_set_event_handler_f(timer, say_hello);
puts("starting timer\n");
}
void
test_countdown(void)
{
dispatch_apply_f(15,
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0),
NULL, final_countdown);
}
int
main(int argc, char **argv)
{
while (argc) {
#if TODO
if (strcmp(argv[0], "--no-proc") == 0)
test_proc = 0;
#endif
argv++;
argc--;
}
test_timer();
dispatch_main();
printf("\n---\n"
"+OK All %d tests completed.\n", testnum - 1);
return (0);
}

View File

@ -19,7 +19,7 @@
#include "common.h"
int testnum = 1;
char *cur_test_id = "undef";
char *cur_test_id = NULL;
int kqfd;
extern void test_evfilt_read();
@ -39,7 +39,6 @@ test_no_kevents(void)
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) {
@ -80,7 +79,7 @@ kevent_fflags_dump(struct kevent *kev)
/* Not every filter has meaningful fflags */
if (kev->filter != EVFILT_VNODE) {
snprintf(buf, 1024, "fflags = %d", kev->flags);
snprintf(buf, 1024, "fflags = %d", kev->fflags);
return (buf);
}
@ -134,24 +133,51 @@ kevent_flags_dump(struct kevent *kev)
return (buf);
}
/* Copied from ../kevent.c kevent_dump() and improved */
/* TODO - backport changes from src/common/kevent.c kevent_dump() */
const char *
kevent_to_str(struct kevent *kev)
{
char buf[512];
snprintf(&buf[0], sizeof(buf), "[filter=%d,%s,%s,ident=%u,data=%d,udata=%p]",
snprintf(&buf[0], sizeof(buf),
"[ident=%d, filter=%d, %s, %s, data=%d, udata=%p]",
(u_int) kev->ident,
kev->filter,
kevent_flags_dump(kev),
kevent_fflags_dump(kev),
(u_int) kev->ident,
(int) kev->data,
kev->udata);
return (strdup(buf));
}
void
kevent_add(int kqfd, struct kevent *kev,
uintptr_t ident,
short filter,
u_short flags,
u_int fflags,
intptr_t data,
void *udata)
{
EV_SET(kev, ident, filter, flags, fflags, data, NULL);
if (kevent(kqfd, kev, 1, NULL, 0, NULL) < 0) {
printf("Unable to add the following kevent:\n%s\n",
kevent_to_str(kev));
err(1, "kevent(): %s", strerror(errno));
}
}
void
kevent_cmp(struct kevent *k1, struct kevent *k2)
{
/* XXX-
Workaround for inconsistent implementation of kevent(2)
*/
#ifdef __FreeBSD__
if (k1->flags & EV_ADD)
k2->flags |= EV_ADD;
#endif
if (memcmp(k1, k2, sizeof(*k1)) != 0) {
printf("kevent_cmp: mismatch:\n %s !=\n %s\n",
kevent_to_str(k1), kevent_to_str(k2));
@ -162,14 +188,21 @@ kevent_cmp(struct kevent *k1, struct kevent *k2)
void
test_begin(const char *func)
{
cur_test_id = (char *) func;
if (cur_test_id)
free(cur_test_id);
cur_test_id = strdup(func);
if (!cur_test_id)
err(1, "strdup failed");
printf("\n\nTest %d: %s\n", testnum++, func);
}
void
success(const char *func)
success(void)
{
printf("%-70s %s\n", func, "passed");
printf("%-70s %s\n", cur_test_id, "passed");
free(cur_test_id);
cur_test_id = NULL;
}
void
@ -179,7 +212,7 @@ test_kqueue(void)
if ((kqfd = kqueue()) < 0)
err(1, "kqueue()");
test_no_kevents();
success("kqueue()");
success();
}
void
@ -188,13 +221,41 @@ test_kqueue_close(void)
test_begin("close(kq)");
if (close(kqfd) < 0)
err(1, "close()");
success("kqueue_close()");
success();
}
void
test_ev_receipt(void)
{
int kq;
struct kevent kev;
test_begin("EV_RECEIPT");
#if HAVE_EV_RECEIPT
if ((kq = kqueue()) < 0)
err(1, "kqueue");
EV_SET(&kev, SIGUSR2, EVFILT_SIGNAL, EV_ADD | EV_RECEIPT, 0, 0, NULL);
if (kevent(kq, &kev, 1, &kev, 1, NULL) < 0)
err(1, "%s", test_id);
/* TODO: check the receipt */
close(kq);
#else
memset(&kev, 0, sizeof(kev));
puts("Skipped -- EV_RECEIPT is not available");
#endif
success();
}
int
main(int argc, char **argv)
{
int test_proc = 1;
int test_proc = 0; /* XXX-FIXME */
int test_socket = 1;
int test_signal = 1;
int test_vnode = 1;
@ -221,13 +282,6 @@ main(int argc, char **argv)
test_kqueue();
test_kqueue_close();
#if FIXME
if (test_proc)
test_evfilt_proc();
puts("All proc tests OK");
exit(0);
#endif
if (test_socket)
test_evfilt_read();
if (test_signal)
@ -240,6 +294,10 @@ main(int argc, char **argv)
#endif
if (test_timer)
test_evfilt_timer();
if (test_proc)
test_evfilt_proc();
test_ev_receipt();
printf("\n---\n"
"+OK All %d tests completed.\n", testnum - 1);

View File

@ -16,63 +16,82 @@
#include "common.h"
static int sigusr1_caught = 0;
int kqfd;
pid_t child;
static void
sig_handler(int signum)
{
sigusr1_caught = 1;
}
static void
add_and_delete(void)
{
const char *test_id1 = "kevent(EVFILT_PROC, EV_ADD)";
const char *test_id2 = "kevent(EVFILT_PROC, EV_ADD)";
struct kevent kev;
pid_t pid;
test_begin(test_id1);
/* Create a child that waits to be killed and then exits */
pid = fork();
if (pid == 0) {
pause();
exit(2);
}
printf(" -- child created (pid %d)\n", (int) pid);
EV_SET(&kev, child, EVFILT_PROC, EV_ADD, 0, 0, NULL);
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id1);
test_begin("kevent(EVFILT_PROC, EV_ADD)");
success(test_id1);
test_no_kevents();
kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_ADD, 0, 0, NULL);
test_no_kevents();
test_begin(test_id2);
success();
kev.flags = EV_DELETE;
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id2);
test_begin("kevent(EVFILT_PROC, EV_DELETE)");
success(test_id2);
}
#if TODO
void
test_kevent_signal_get(void)
{
const char *test_id = "kevent(EVFILT_SIGNAL, wait)";
struct kevent kev;
test_begin(test_id);
EV_SET(&kev, SIGUSR1, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
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)
test_no_kevents();
kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_DELETE, 0, 0, NULL);
if (kill(pid, SIGKILL) < 0)
err(1, "kill");
sleep(1);
test_no_kevents();
kev.flags |= EV_CLEAR;
kev.data = 1;
kevent_cmp(&kev, kevent_get(kqfd));
success();
success(test_id);
}
static void
event_trigger(void)
{
struct kevent kev;
pid_t pid;
test_begin("kevent(EVFILT_PROC, wait)");
/* Create a child that waits to be killed and then exits */
pid = fork();
if (pid == 0) {
pause();
printf(" -- child caught signal, exiting\n");
exit(2);
}
printf(" -- child created (pid %d)\n", (int) pid);
test_no_kevents();
kevent_add(kqfd, &kev, pid, EVFILT_PROC, EV_ADD, 0, 0, NULL);
/* Cause the child to exit, then retrieve the event */
printf(" -- killing process %d\n", (int) pid);
if (kill(pid, SIGUSR1) < 0)
err(1, "kill");
kevent_cmp(&kev, kevent_get(kqfd));
test_no_kevents();
success();
}
#ifdef TODO
void
test_kevent_signal_disable(void)
{
@ -91,12 +110,12 @@ test_kevent_signal_disable(void)
sigaddset(&mask, SIGUSR1);
if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1)
err(1, "sigprocmask");
if (kill(getpid(), SIGUSR1) < 0)
if (kill(getpid(), SIGKILL) < 0)
err(1, "kill");
test_no_kevents();
success(test_id);
success();
}
void
@ -133,7 +152,7 @@ test_kevent_signal_enable(void)
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
success(test_id);
success();
}
void
@ -159,7 +178,7 @@ test_kevent_signal_del(void)
err(1, "kill");
test_no_kevents();
success(test_id);
success();
}
void
@ -192,7 +211,7 @@ test_kevent_signal_oneshot(void)
err(1, "kill");
test_no_kevents();
success(test_id);
success();
}
#endif
@ -201,14 +220,13 @@ test_evfilt_proc()
{
kqfd = kqueue();
/* Create a child that sleeps for a few seconds and then exits */
child = fork();
if (child == 0) {
sleep(3);
exit(123);
}
signal(SIGUSR1, sig_handler);
add_and_delete();
event_trigger();
signal(SIGUSR1, SIG_DFL);
#if TODO
test_kevent_signal_add();
test_kevent_signal_del();

View File

@ -50,7 +50,7 @@ test_kevent_socket_add(void)
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
success(test_id);
success();
}
void
@ -77,7 +77,7 @@ test_kevent_socket_get(void)
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
success(test_id);
success();
}
void
@ -111,7 +111,7 @@ test_kevent_socket_clear(void)
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
success(test_id);
success();
}
void
@ -147,7 +147,7 @@ test_kevent_socket_disable_and_enable(void)
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
success(test_id);
success();
}
void
@ -166,7 +166,7 @@ test_kevent_socket_del(void)
test_no_kevents();
kevent_socket_drain();
success(test_id);
success();
}
void
@ -199,7 +199,7 @@ test_kevent_socket_oneshot(void)
kevent_socket_drain();
success(test_id);
success();
}
@ -223,7 +223,6 @@ test_kevent_socket_dispatch(void)
/* The event will occur only once, even though EV_CLEAR is not
specified. */
kevent_socket_fill();
kev.flags = EV_ADD; /* FIXME: not sure what the proper behavior is */
kev.data = 1;
kevent_cmp(&kev, kevent_get(kqfd));
test_no_kevents();
@ -235,7 +234,7 @@ test_kevent_socket_dispatch(void)
kevent_socket_drain();
success(test_id);
success();
}
#endif /* HAVE_EV_DISPATCH */
@ -269,7 +268,7 @@ test_kevent_socket_lowat(void)
kevent_socket_drain();
kevent_socket_drain();
success(test_id);
success();
}
#endif
@ -298,14 +297,14 @@ test_kevent_socket_eof(void)
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
success(test_id);
success();
}
void
test_evfilt_read()
{
/* Create a connected pair of full-duplex sockets for testing socket events */
if (socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd) < 0)
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockfd) < 0)
abort();
kqfd = kqueue();

View File

@ -30,7 +30,7 @@ test_kevent_signal_add(void)
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
success(test_id);
success();
}
void
@ -58,7 +58,7 @@ test_kevent_signal_get(void)
kev.data = 1;
kevent_cmp(&kev, kevent_get(kqfd));
success(test_id);
success();
}
void
@ -84,7 +84,7 @@ test_kevent_signal_disable(void)
test_no_kevents();
success(test_id);
success();
}
void
@ -121,7 +121,7 @@ test_kevent_signal_enable(void)
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
success(test_id);
success();
}
void
@ -147,7 +147,7 @@ test_kevent_signal_del(void)
err(1, "kill");
test_no_kevents();
success(test_id);
success();
}
void
@ -180,7 +180,7 @@ test_kevent_signal_oneshot(void)
err(1, "kill");
test_no_kevents();
success(test_id);
success();
}
void

View File

@ -30,7 +30,7 @@ test_kevent_timer_add(void)
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
success(test_id);
success();
}
void
@ -47,7 +47,7 @@ test_kevent_timer_del(void)
test_no_kevents();
success(test_id);
success();
}
void
@ -70,7 +70,7 @@ test_kevent_timer_get(void)
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
success(test_id);
success();
}
static void
@ -97,7 +97,7 @@ test_oneshot(void)
test_no_kevents();
success(test_id);
success();
}
static void
@ -128,7 +128,7 @@ test_periodic(void)
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
success(test_id);
success();
}
static void
@ -159,7 +159,7 @@ disable_and_enable(void)
kev.data = 1;
kevent_cmp(&kev, kevent_get(kqfd));
success(test_id);
success();
}
void

View File

@ -26,17 +26,13 @@ add_and_delete(void)
test_begin(test_id);
EV_SET(&kev, 1, EVFILT_USER, EV_ADD, 0, 0, NULL);
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_ADD, 0, 0, NULL);
test_no_kevents();
kev.flags = EV_DELETE;
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_DELETE, 0, 0, NULL);
test_no_kevents();
success(test_id);
success();
}
static void
@ -47,17 +43,20 @@ event_wait(void)
test_begin(test_id);
EV_SET(&kev, 1, EVFILT_USER, EV_ADD, 0, 0, NULL);
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
test_no_kevents();
kev.fflags |= NOTE_TRIGGER;
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
/* Add the event, and then trigger it */
kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_ADD | EV_CLEAR, 0, 0, NULL);
kevent_add(kqfd, &kev, 1, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL);
kev.fflags &= ~NOTE_FFCTRLMASK;
kev.fflags &= ~NOTE_TRIGGER;
kev.flags = EV_CLEAR;
kevent_cmp(&kev, kevent_get(kqfd));
success(test_id);
test_no_kevents();
success();
}
static void
@ -68,25 +67,24 @@ disable_and_enable(void)
test_begin(test_id);
EV_SET(&kev, 1, EVFILT_USER, EV_ADD, 0, 0, NULL);
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
kev.flags = EV_DISABLE;
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
kev.fflags |= NOTE_TRIGGER;
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
test_no_kevents();
kev.flags = EV_ENABLE;
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
kev.flags = EV_ADD;
kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_ADD, 0, 0, NULL);
kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_DISABLE, 0, 0, NULL);
/* Trigger the event, but since it is disabled, nothing will happen. */
kevent_add(kqfd, &kev, 1, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL);
test_no_kevents();
kevent_add(kqfd, &kev, 1, EVFILT_USER, EV_ENABLE, 0, 0, NULL);
kevent_add(kqfd, &kev, 1, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL);
kev.flags = EV_CLEAR;
kev.fflags &= ~NOTE_FFCTRLMASK;
kev.fflags &= ~NOTE_TRIGGER;
kevent_cmp(&kev, kevent_get(kqfd));
success(test_id);
success();
}
static void
@ -97,26 +95,21 @@ oneshot(void)
test_begin(test_id);
EV_SET(&kev, 1, EVFILT_USER, EV_ADD | EV_ONESHOT, 0, 0, NULL);
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
test_no_kevents();
kevent_add(kqfd, &kev, 2, EVFILT_USER, EV_ADD | EV_ONESHOT, 0, 0, NULL);
puts(" -- event 1");
kev.fflags |= NOTE_TRIGGER;
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
kevent_add(kqfd, &kev, 2, EVFILT_USER, 0, NOTE_TRIGGER, 0, NULL);
kev.flags = EV_ONESHOT;
kev.fflags &= ~NOTE_FFCTRLMASK;
kev.fflags &= ~NOTE_TRIGGER;
kevent_cmp(&kev, kevent_get(kqfd));
/* Try to trigger the event again. It is deleted, so that
should be an error.
*/
puts(" -- event 2 (should fail)");
kev.flags = 0;
kev.fflags |= NOTE_TRIGGER;
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) == 0)
err(1, "%s", test_id);
test_no_kevents();
success(test_id);
success();
}
void

View File

@ -40,7 +40,7 @@ test_kevent_vnode_add(void)
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
success(test_id);
success();
}
void
@ -60,7 +60,7 @@ test_kevent_vnode_note_delete(void)
kevent_cmp(&kev, kevent_get(kqfd));
success(test_id);
success();
}
void
@ -84,7 +84,7 @@ test_kevent_vnode_note_write(void)
kev.fflags |= NOTE_EXTEND; // XXX-FIXME compatibility issue
kevent_cmp(&kev, kevent_get(kqfd));
success(test_id);
success();
}
void
@ -112,7 +112,7 @@ test_kevent_vnode_note_attrib(void)
err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)",
test_id, (unsigned int)kev.ident, kev.filter, kev.flags);
success(test_id);
success();
}
void
@ -143,7 +143,7 @@ test_kevent_vnode_note_rename(void)
if (system("mv /tmp/kqueue-test2.tmp /tmp/kqueue-test.tmp") < 0)
err(1, "system");
success(test_id);
success();
}
void
@ -158,7 +158,7 @@ test_kevent_vnode_del(void)
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
success(test_id);
success();
}
void
@ -200,7 +200,7 @@ test_kevent_vnode_disable_and_enable(void)
err(1, "%s - incorrect event (sig=%u; filt=%d; flags=%d)",
test_id, (unsigned int)kev.ident, kev.filter, kev.flags);
success(test_id);
success();
}
#if HAVE_EV_DISPATCH
@ -242,7 +242,7 @@ test_kevent_vnode_dispatch(void)
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "remove watch failed: %s", test_id);
success(test_id);
success();
}
#endif /* HAVE_EV_DISPATCH */

View File

@ -7,7 +7,33 @@
<title>libkqueue</title>
<style type="text/css">
body {
margin-left: 2em;
padding-bottom: 2em;
}
.blockquote { margin-right: 2em }
code.block {
padding: 20px;
border: 1px solid black;
background: beige;
margin: 3em;
}
h1 {
margin-left: -0.5em;
}
h2 {
margin-left: -0.5em;
padding-top: 1em;
}
ol {
margin: 2em;
}
li {
padding-bottom: 10px;
}
p {
padding-bottom: 10px;
}
</style>
</head>
@ -15,21 +41,37 @@
<h1>libkqueue</h1>
libkqueue is a portable userspace implementation of the <a href="http://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2">kqueue(2)</a> kernel event notification mechanism. Initial efforts are focused on porting the kqueue API to the Linux 2.6 kernel.
libkqueue is a portable userspace implementation of the <a href="http://www.freebsd.org/cgi/man.cgi?query=kqueue&sektion=2">kqueue(2)</a> kernel event notification mechanism. Initial efforts are focused on porting the kqueue API to the Linux 2.6 kernel. There is also an experimental Solaris port (currently broken).
<h2>Download</h2>
Source code releases can be found <a href="dist">here</a>.
Source code releases can be found <a href="dist">here</a>. There are no binary packages at the moment.
<p>
To checkout the SVN repository, run the following command:
To checkout the entire SVN repository, run the following command:
<p>
<code>svn checkout svn://mark.heily.com/libkqueue</code>
<code class=block>svn co svn://mark.heily.com/libkqueue</code>
<p>
You can checkout the trunk by using the following command:
<p>
<code class=block>svn co svn://mark.heily.com/libkqueue/trunk libkqueue</code>
<p>
There is heavy development on the trunk, and it may not always be in a usable state. If you want to be current, but not bleeding edge, there is also a stable branch. You can checkout the stable branch by using the following command:
<p>
<code class=block>svn co svn://mark.heily.com/libkqueue/branches/stable libkqueue</code>
<h2>Mailing Lists</h2>
There are two mailing lists: one for
<a href="http://groups.google.com/group/libkqueue">general discussion</a>, and one for
<a href="http://groups.google.com/group/libkqueue-announce">announcements only</a>.
There are two mailing lists: one for general discussion, and one for announcements. If you subscribe to a mailing list through the web interface, you will need to sign up for a free Google account. If you prefer not to create a Google account, you can subscribe via email instead.
<p>
To subscribe to the general discussion group, visit the group's <a href="http://groups.google.com/group/libkqueue">web interface</a>, or send an email to <a href="mailto:libkqueue+subscribe@googlegroups.com">libkqueue+subscribe@googlegroups.com</a>.
<p>
To subscribe to the low-traffic annoucements-only group, visit the group's <a href="http://groups.google.com/group/libkqueue-announce">web interface</a>, or send an email to <a href="mailto:libkqueue-announce+subscribe@googlegroups.com">libkqueue-announce+subscribe@googlegroups.com</a>.
<p>
Please note that if you use multiple email addresses, Google Groups does not allow you to choose which email address will receive messages. The address on the <code>From:</code> line of the subscribe request will be used as the destination address.
<p>
To unsubscribe from either group, send an email to <code>[group name]+unsubscribe@googlegroups.com</code>. If you have any problems subscribing or unsubscribing, send a note to <a href="mailto:devel+libkqueue@heily.com">Mark Heily</a>.
<h2>Status</h2>
@ -38,15 +80,17 @@ The kqueue and kevent functions are implemented, and most of the filters are wor
<ul>
<li><code>EVFILT_PROC</code>
<li><code>EVFILT_AIO</code>
<li><code>EVFILT_FS</code>
<li><code>EVFILT_NETDEV</code>
</ul>
More details about the current implementation status can be found <a href="status.html">here</a>.
<p>
There are several compatibility issues to be aware of when using this library under Linux:<p>
There are several compatibility issues to be aware of when using this library under Linux:<br>
<ol>
<li>The NOTE_LOWAT flag is not supported. According to <i>socket(7)</i>,<p>
<li>The NOTE_LOWAT flag is not supported. According to <i>socket(7)</i>,<br>
<pre style="blockquote">
"The select(2) and poll(2) system calls currently do not respect the
SO_RCVLOWAT setting on Linux, and mark a socket readable when even a
@ -72,6 +116,46 @@ The EVFILT_PROC filter only supports the NOTE_EXIT flag, and the <code>ident</co
</ol>
Here are the issues to be aware of when using this library under any platform:<br>
<ol>
<li>When a kqueue descriptor is closed, it's resources are not immediately reclaimed. Instead,
they will be freed the next time that the <code>kqueue(2)</code> function is called.
</li>
</ol>
<h2>Future Plans</h2>
In the future, I plan to add three more backends. One will be for Solaris and take advantage of the event port mechanism described in <a href="http://docs.sun.com/app/docs/doc/816-5168/port-create-3c?l=en&a=view&q=port_create">port_create(3C)</a>. The second will be a generic POSIX backend that does not use any non-portable system calls. The third will be a Microsoft Windows port that uses <code>WaitForMultipleObjects()</code>.
<h2>Portability of kevent(2) across BSD systems</h2>
There are some differences in the behavior of the <code>kevent(2)</code> system call across the various BSD-based operating systems. Here are some of the differences to be aware of:
<ol>
<li>FreeBSD 8 does not set the <i>EV_ADD</i> flag for kevents on the eventlist, but OpenBSD and Darwin do. This means you should never use the equality operator (==) to test the flags; use the logical AND operator instead.
</li>
<li>The <code>EVFILT_USER</code> filter behaves differently from other filters with respect to the <i>EV_ONESHOT</i> flag. All other filters will preserve the flag when the event is triggered and placed on the eventlist. The <code>EVFILT_USER</code> filter does not preserve this flag.
</li>
<li>
OpenBSD has the <i>NOTE_TRUNCATE</i> fflag, while FreeBSD and Darwin do not.
</li>
<li>
EVFILT_FS is undocumented and only available on FreeBSD and Darwin. Here is the <a href="http://adam.kungfoohampster.com/lists/cvs-all/msg71399.shtml">CVS commit log</a> which could be helpful to document this filter.
</li>
<!--
<li>
[TODO - Verify and check against 10.6] OS X 10.5 and earlier do not implement the <code>EVFILT_AIO</code> or <code>EVFILT_TIMER</code> filters.
</li>
-->
</ol>
<h2>Requirements</h2>
libkqueue currently requires the following:
@ -82,14 +166,49 @@ libkqueue currently requires the following:
<li>glibc 2.8 or higher
</ul>
The Solaris port requires Solaris 10 or higher, and uses the GNU compiler and toolchain.
<h2>Usage</h2>
Here are the steps to use libkqueue in your program:
<h3>Ordinary Makefile</h3>
Here are the steps to use libkqueue in your program if you use an ordinary Makefile:
<ol>
<li>Add <code>`pkg-config libkqueue --cflags`</code> to the CFLAGS variable.
<li>Add <code>`pkg-config libkqueue --libs`</code> to the LDADD variable.
<li>Add <code>#include &lt;sys/event.h&gt;</code> to the source code.
</ol>
<p>
<h3>Autoconf/Automake/Libtool</h3>
If your program uses the GNU Autoconf/Automake/Libtool build system, the following steps will allow you to use libkqueue:
<ol>
<li>Add the following to <code>configure.ac</code>:
<pre>
#
# Prefer native kqueue(2); otherwise use libkqueue if present.
#
AC_CHECK_HEADER(sys/event.h, [],
[PKG_CHECK_MODULES(KQUEUE, libkqueue)]
)
</pre>
</li>
<li>
Add the following to <code>Makefile.am</code> (assuming your program is named "foo"):
<pre>
foo_CFLAGS+=$(KQUEUE_CFLAGS)
foo_LDADD+=$(KQUEUE_LIBS)
</pre>
</li>
</ol>
<p>Instead of using the <code>$(KQUEUE_LIBS)</code> variable, you could just add <code>libkqueue.la</code> to your programs LDADD variable.
<h3>Threads</h3>
@ -106,8 +225,31 @@ libkqueue uses one or more helper threads, so all programs that link with libkqu
<li><a href="http://doc.geoffgarside.co.uk/kqueue/">Kqueues for Fun and Profit</a> [Werner]</li>
</ul>
<li>
<a href="http://www.opensource.apple.com/source/xnu/xnu-1456.1.26/tools/tests/xnu_quick_test/kqueue_tests.c">XNU kqueue test program</a> - Demonstrates use of EVFILT_USER
</li>
<h3>Solaris Event Ports</h3>
I do plan to add Solaris event port support to libkqueue once the Linux backend is complete and the internal API is stabilized. I checked the port_create(3C) manpage in Solaris 10 and OpenSolaris, and did not see any way to wait for signals. There is also no equivalent to EVFILT_VNODE in Solaris 10, although OpenSolaris has the PORT_SOURCE_FILE functionality.
<ul>
<li>
<a href="http://docs.sun.com/app/docs/doc/816-5168/port-create-3c?l=en&a=view&q=port_create">Solaris 10 port_create(3C) manual page</a>
</li>
<li>
<a href="http://docs.sun.com/app/docs/doc/819-2243/port-create-3c?a=view">
OpenSolaris port_create(3C) manual page</a>
</li>
<h2>Contact</h2>
For more information, contact <a href="mailto:devel@heily.com">Mark Heily</a>.
For more information, contact <a href="mailto:devel+libkqueue@heily.com">Mark Heily</a>.
<p>
<hr>
<center>
&copy; 2009 Mark Heily. All rights reserved.
</center>
</body>
</html>