summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/libc/sys/kqueue.2400
-rw-r--r--sys/kern/kern_event.c913
-rw-r--r--sys/sys/event.h165
3 files changed, 1478 insertions, 0 deletions
diff --git a/lib/libc/sys/kqueue.2 b/lib/libc/sys/kqueue.2
new file mode 100644
index 000000000000..e0b53d5919d0
--- /dev/null
+++ b/lib/libc/sys/kqueue.2
@@ -0,0 +1,400 @@
+.\" Copyright (c) 2000 Jonathan Lemon
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd April 14, 2000
+.Dt KQUEUE 2
+.Os
+.Sh NAME
+.Nm kqueue, kevent
+.Nd kernel event notification mechanism
+.Sh SYNOPSIS
+.Fd #include <sys/event.h>
+.Ft int
+.Fn kqueue "void"
+.Ft int
+.Fn kevent "int kq" "int nchanges" "struct kevent **changelist" \
+"int nevents" "struct kevent *eventlist" "struct timespec *timeout"
+.Sh DESCRIPTION
+.Fn kqueue
+provides a generic method of notifying the user when an event
+happens or a condition holds, based on the results of small
+pieces of kernel code termed filters.
+An kevent is identified by the (ident, filter) pair; there may only
+be one unique kevent per kqueue.
+
+The filter is executed upon the initial registration of a kevent
+in order to detect a preexisting condition is present, and is also
+executed whenever an event is passed to the filter for evaluation.
+If the filter determines that the condition should be reported,
+then the kevent is placed on the kqueue for the user to retrieve.
+
+The filter is also run when the user attempts to retrieve the kevent
+from the kqueue, and if the filter indicates the condition that triggered
+the event no longer holds, the kevent is removed from the kqueue and
+is not returned.
+
+Multiple events which trigger the filter do not result in multiple
+kevents being placed on the kqueue; instead, the filter will aggregate
+the events into a single struct kevent.
+Calling
+.Fn close
+on a file descriptor will remove any kevents that reference the descriptor.
+.Pp
+.Fn kqueue
+creates a new kernel event queue and returns a descriptor.
+.Pp
+.Fn kevent
+is used to register events with the queue, and return any pending
+events to the user.
+.Fa changelist
+is a pointer to an array of pointers to
+.Va kevent
+structures, as defined in
+.Aq Pa event.h .
+All changes contained in the
+.Fa changelist
+are applied before any pending events are read from the queue.
+.Fa nchanges
+gives the size of
+.Fa changelist .
+.Fa eventlist
+is a pointer to an array of kevent structures.
+.Fa nevents
+determines the size of
+.Fa eventlist .
+If
+.Fa timeout
+is a non-NULL pointer, it specifies a maximum interval to wait
+for an event. If
+.Fa timeout
+is a NULL pointer,
+.Fn kevent
+waits indefinitely. To effect a poll, the
+.Fa timeout
+argument should be non-NULL, pointing to a zero-valued
+.Va timespec
+structure.
+.Pp
+The
+.Va kevent
+structure is defined as:
+.Bd -literal
+struct kevent {
+ uintptr_t ident; /* identifier for this event */
+ short filter; /* filter for event */
+ u_short flags; /* action flags for kqueue */
+ u_int fflags; /* filter flag value */
+ intptr_t data; /* filter data value */
+ void *udata; /* opaque user data identifier */
+};
+.Ed
+.Pp
+The fields of
+.Fa struct kevent
+are:
+.Bl -tag -width XXXfilter
+.It ident
+Value used to identify this event.
+The exact interpretation is determined by the attached filter,
+but often is a file descriptor.
+.It filter
+Identifies the kernel filter used to process this event. The pre-defined
+system filters are described below.
+.It flags
+Actions to perform on the event.
+.It fflags
+Filter-specific flags.
+.It data
+Filter-specific data value.
+.It udata
+Opaque user-defined value passed through the kernel unchanged.
+.El
+.Pp
+The
+.Va flags
+field can contain the following values:
+.Bl -tag -width XXXEV_ONESHOT
+.It EV_ADD
+Adds the event to the kqueue. Re-adding an existing event
+will modify the parameters of the original event, and not result
+in a duplicate entry. Adding an event automatically enables it,
+unless overridden by the EV_DISABLE flag.
+.It EV_ENABLE
+Permit
+.Fn kevent
+to return the event if it is triggered.
+.It EV_DISABLE
+Disable the event so
+.Fn kevent
+will not return it. The filter itself is not disabled.
+.It EV_DELETE
+Removes the event from the kqueue. Events which are attached to
+file descriptors are automatically deleted on the last close of
+the descriptor.
+.It EV_ONESHOT
+Causes the event to return only the first occurrence of the filter
+being triggered. After the user retrieves the event from the kqueue,
+it is deleted.
+.It EV_CLEAR
+After the event is retrieved by the user, its state is reset.
+This is useful for filters which report state transitions
+instead of the current state. Note that some filters may automatically
+set this flag internally.
+.It EV_EOF
+Filters may set this flag to indicate filter-specific EOF condition.
+.It EV_ERROR
+See
+.Sx RETURN VALUES
+below.
+.El
+.Pp
+The predefined system filters are listed below.
+Arguments may be passed to and from the filter via the
+.Va fflags
+and
+.Va data
+fields in the kevent structure.
+.Bl -tag
+.It EVFILT_READ
+Takes a descriptor as the identifier, and returns whenever
+there is data available to read.
+The behavior of the filter is slightly different depending
+on the descriptor type.
+.Bl -tag
+.It Sockets
+Sockets which have previously been passed to
+.Fn listen
+return when there is an incoming connection pending.
+.Va data
+contains the size of the listen backlog.
+.Pp
+Other socket descriptors return when there is data to be read,
+subject to the SO_RCVLOWAT value of the socket buffer.
+.Va data
+contains the number of bytes in the socket buffer.
+.Pp
+If the read direction of the socket has shutdown, then the filter
+also sets EV_EOF in
+.Va flags .
+It is possible for EOF to be returned (indicating the connection is gone)
+while there is still data pending in the socket buffer.
+.El
+.Bl -tag
+.It Vnodes
+Returns when the file pointer is not at the end of file.
+.Va data
+contains the offset from current position to end of file,
+and may be negative.
+.El
+.Bl -tag
+.It Fifos, Pipes
+Returns when the there is data to read;
+.Va data
+contains the number of bytes available.
+.Pp
+When the last writer disconnects, the filter will set EV_EOF in
+.Va flags .
+This may be cleared by passing in EV_CLEAR, at which point the
+filter will resume waiting for data to become available before
+returning.
+.El
+.El
+.Bl -tag
+.It EVFILT_WRITE
+Takes a descriptor as the identifier, and returns whenever
+it is possible to write to the descriptor. For sockets, pipes
+and fifos,
+.Va data
+will contain the amount of space remaining in the write buffer.
+The filter will set EV_EOF when the reader disconnects, and for
+the fifo case, this may be cleared by use of EV_CLEAR.
+Note that this filter is not supported for vnodes.
+.El
+.Bl -tag
+.It EVFILT_AIO
+A kevent structure is initialized, with
+.Va ident
+containing the descriptor of the kqueue that the event should be
+attached to. The address of the kevent structure is then placed in the
+.Va aio_lio_opcode
+field of the AIO request, and the aio_* function is then called.
+The event will be registered with the specified kqueue, and the
+.Va ident
+argument set to the
+.Fa struct aiocb
+returned by the aio_* function.
+The filter returns under the same conditions as aio_error.
+.Pp
+NOTE: this interface is unstable and subject to change.
+.El
+.Bl -tag
+.It EVFILT_VNODE
+Takes a file descriptor as the identifier and the events to watch for in
+.Va fflags ,
+and returns when one or more of the requested events occurs on the descriptor.
+The events to monitor are:
+.Bl -tag -width XXNOTE_RENAME
+.It NOTE_DELETE
+.Fn unlink
+was called on the file referenced by the descriptor.
+.It NOTE_WRITE
+A write occurred on the file referenced by the descriptor.
+.It NOTE_EXTEND
+The file referenced by the descriptor was extended.
+.It NOTE_ATTRIB
+The file referenced by the descriptor had its attributes changed.
+.It NOTE_LINK
+The link count on the file changed.
+.It NOTE_RENAME
+The file referenced by the descriptor was renamed.
+.El
+.Pp
+On return,
+.Va fflags
+contains the events which triggered the filter.
+.El
+.Bl -tag
+.It EVFILT_PROC
+Takes the process ID to monitor as the identifier and the events to watch for
+in
+.Va fflags ,
+and returns when the process performs one or more of the requested events.
+If a process can normally see another process, it can attach an event to it.
+The events to monitor are:
+.Bl -tag -width XXNOTE_TRACKERR
+.It NOTE_EXIT
+The process has exited.
+.It NOTE_FORK
+The process has called
+.Fn fork .
+.It NOTE_EXEC
+The process has executed a new process via
+.Xr execve 2
+or similar call.
+.It NOTE_TRACK
+Follow a process across
+.Fn fork
+calls. The parent process will return with NOTE_TRACK set in the
+.Va fflags
+field, while the child process will return with NOTE_CHILD set in
+.Va fflags
+and the parent PID in
+.Va data .
+.It NOTE_TRACKERR
+This flag is returned if the system was unable to attach an event to
+the child process, usually due to resource limitations.
+.El
+.Pp
+On return,
+.Va fflags
+contains the events which triggered the filter.
+.It EVFILT_SIGNAL
+Takes the signal number to monitor as the identifier and returns
+when the given signal is delivered to the process.
+This coexists with the
+.Fn signal
+and
+.Fn sigaction
+facilities, and has a lower precedence. The filter will record
+all attempts to deliver a signal to a process, even if the signal has
+been marked as SIG_IGN. Event notification happens after normal
+signal delivery processing.
+.Va data
+returns the number of times the signal has occurred since the last call to
+.Fn kqueue .
+This filter automatically sets the EV_CLEAR flag internally.
+.El
+.Sh RETURN VALUES
+.Fn kevent
+returns the number of events placed in the
+.Ar eventlist ,
+up to the value given by
+.Ar nevents .
+If an error occurs while processing an element of the
+.Ar changelist
+and there is enough room in the
+.Ar eventlist ,
+then the event will be placed in the
+.Ar eventlist
+with
+.Dv EV_ERROR
+set in
+.Va flags
+and the system error in
+.Va data .
+Otherwise,
+.Dv -1
+will be returned, and
+.Dv errno
+will be set to indicate the error condition.
+If the time limit expires, then
+.Fn kevent
+returns 0.
+.Sh ERRORS
+The
+.Fn kevent
+function fails if:
+.Bl -tag -width Er
+.It Bq Er EACCESS
+The process does not have permission to register a filter.
+.It Bq Er EFAULT
+There was an error reading or writing the
+.Va kevent
+structure.
+.It Bq Er EBADF
+The specified descriptor is invalid.
+.It Bq Er EINTR
+A signal was delivered before the timeout expired and before any
+events were placed on the kqueue for return.
+.It Bq Er EINVAL
+The specified time limit or filter is invalid.
+.It Bq Er ENOMEM
+No memory was available to register the event.
+.It Bq Er ESRCH
+The specified process to attach to does not exist.
+.El
+.Sh SEE ALSO
+.Xr aio_error 2 ,
+.Xr aio_read 2 ,
+.Xr aio_return 2 ,
+.Xr poll 2 ,
+.Xr read 2 ,
+.Xr select 2 ,
+.Xr signal 3 ,
+.Xr sigaction 2 ,
+.Xr write 2 .
+.Sh HISTORY
+The
+.Fn kqueue
+and
+.Fn kevent
+functions first appeared in
+.Fx 5.0 .
+.Sh AUTHOR
+The
+.Fn kqueue
+system and this manual page were written by
+.An Jonathan Lemon Aq jlemon@freebsd.org .
diff --git a/sys/kern/kern_event.c b/sys/kern/kern_event.c
new file mode 100644
index 000000000000..c22b24878cc6
--- /dev/null
+++ b/sys/kern/kern_event.c
@@ -0,0 +1,913 @@
+/*-
+ * Copyright (c) 1999,2000 Jonathan Lemon <jlemon@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/proc.h>
+#include <sys/malloc.h>
+#include <sys/unistd.h>
+#include <sys/file.h>
+#include <sys/fcntl.h>
+#include <sys/select.h>
+#include <sys/queue.h>
+#include <sys/event.h>
+#include <sys/eventvar.h>
+#include <sys/poll.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#include <sys/stat.h>
+#include <sys/sysproto.h>
+#include <sys/uio.h>
+
+#include <vm/vm_zone.h>
+
+static int filt_nullattach(struct knote *kn);
+static int filt_rwtypattach(struct knote *kn);
+static int filt_kqattach(struct knote *kn);
+static void filt_kqdetach(struct knote *kn);
+static int filt_kqueue(struct knote *kn, long hint);
+static int filt_procattach(struct knote *kn);
+static void filt_procdetach(struct knote *kn);
+static int filt_proc(struct knote *kn, long hint);
+
+static int kqueue_create(struct kqueue **kqp);
+static int kqueue_scan(struct file *fp, int maxevents,
+ struct kevent *ulistp, struct timespec *timeout,
+ struct proc *p);
+static int kqueue_read(struct file *fp, struct uio *uio,
+ struct ucred *cred, int flags, struct proc *p);
+static int kqueue_write(struct file *fp, struct uio *uio,
+ struct ucred *cred, int flags, struct proc *p);
+static int kqueue_ioctl(struct file *fp, u_long com, caddr_t data,
+ struct proc *p);
+static int kqueue_poll(struct file *fp, int events, struct ucred *cred,
+ struct proc *p);
+static int kqueue_stat(struct file *fp, struct stat *st, struct proc *p);
+static int kqueue_close(struct file *fp, struct proc *p);
+static void kqueue_wakeup(struct kqueue *kq);
+
+static void knote_attach(struct knote *kn, struct filedesc *fdp);
+static void knote_drop(struct knote *kn, struct proc *p);
+static void knote_enqueue(struct knote *kn);
+static void knote_dequeue(struct knote *kn);
+static void knote_init(void);
+static struct knote *knote_alloc(void);
+static void knote_free(struct knote *kn);
+
+static vm_zone_t knote_zone;
+
+#define KNOTE_ACTIVATE(kn) do { \
+ kn->kn_status |= KN_ACTIVE; \
+ if ((kn->kn_status & (KN_QUEUED | KN_DISABLED)) == 0) \
+ knote_enqueue(kn); \
+} while(0)
+
+#define KN_HASHSIZE 64 /* XXX should be tunable */
+#define KN_HASH(val, mask) (((val) ^ (val >> 8)) & (mask))
+
+static struct fileops kqueueops = {
+ kqueue_read,
+ kqueue_write,
+ kqueue_ioctl,
+ kqueue_poll,
+ kqueue_stat,
+ kqueue_close
+};
+
+extern struct filterops so_rwfiltops[];
+extern struct filterops fifo_rwfiltops[];
+extern struct filterops pipe_rwfiltops[];
+extern struct filterops vn_rwfiltops[];
+
+static struct filterops kq_rwfiltops[] = {
+ { 1, filt_kqattach, filt_kqdetach, filt_kqueue },
+ { 1, filt_nullattach, NULL, NULL },
+};
+
+extern struct filterops aio_filtops;
+extern struct filterops sig_filtops;
+extern struct filterops vn_filtops;
+
+static struct filterops rwtype_filtops =
+ { 1, filt_rwtypattach, NULL, NULL };
+static struct filterops proc_filtops =
+ { 0, filt_procattach, filt_procdetach, filt_proc };
+
+/*
+ * XXX
+ * These must match the order of defines in <sys/file.h>
+ */
+static struct filterops *rwtypfilt_sw[] = {
+ NULL, /* 0 */
+ vn_rwfiltops, /* DTYPE_VNODE */
+ so_rwfiltops, /* DTYPE_SOCKET */
+ pipe_rwfiltops, /* DTYPE_PIPE */
+ fifo_rwfiltops, /* DTYPE_FIFO */
+ kq_rwfiltops, /* DTYPE_KQUEUE */
+};
+
+/*
+ * table for for all system-defined filters.
+ */
+static struct filterops *sysfilt_ops[] = {
+ &rwtype_filtops, /* EVFILT_READ */
+ &rwtype_filtops, /* EVFILT_WRITE */
+ &aio_filtops, /* EVFILT_AIO */
+ &vn_filtops, /* EVFILT_VNODE */
+ &proc_filtops, /* EVFILT_PROC */
+ &sig_filtops, /* EVFILT_SIGNAL */
+};
+
+static int
+filt_nullattach(struct knote *kn)
+{
+ return (ENXIO);
+}
+
+/*
+ * file-type specific attach routine for read/write filters
+ */
+static int
+filt_rwtypattach(struct knote *kn)
+{
+ struct filterops *fops;
+
+ fops = rwtypfilt_sw[kn->kn_fp->f_type];
+ if (fops == NULL)
+ return (EINVAL);
+ kn->kn_fop = &fops[~kn->kn_filter]; /* convert to 0-base index */
+ return (kn->kn_fop->f_attach(kn));
+}
+
+static int
+filt_kqattach(struct knote *kn)
+{
+ struct kqueue *kq = (struct kqueue *)kn->kn_fp->f_data;
+
+ SLIST_INSERT_HEAD(&kq->kq_sel.si_note, kn, kn_selnext);
+ return (0);
+}
+
+static void
+filt_kqdetach(struct knote *kn)
+{
+ struct kqueue *kq = (struct kqueue *)kn->kn_fp->f_data;
+
+ SLIST_REMOVE(&kq->kq_sel.si_note, kn, knote, kn_selnext);
+}
+
+/*ARGSUSED*/
+static int
+filt_kqueue(struct knote *kn, long hint)
+{
+ struct kqueue *kq = (struct kqueue *)kn->kn_fp->f_data;
+
+ kn->kn_data = kq->kq_count;
+ return (kn->kn_data > 0);
+}
+
+static int
+filt_procattach(struct knote *kn)
+{
+ struct proc *p;
+
+ p = pfind(kn->kn_id);
+ if (p == NULL)
+ return (ESRCH);
+ if (! PRISON_CHECK(curproc, p))
+ return (EACCES);
+
+ kn->kn_ptr.p_proc = p;
+ kn->kn_flags |= EV_CLEAR; /* automatically set */
+
+ /*
+ * internal flag indicating registration done by kernel
+ */
+ if (kn->kn_flags & EV_FLAG1) {
+ kn->kn_data = kn->kn_sdata; /* ppid */
+ kn->kn_fflags = NOTE_CHILD;
+ kn->kn_flags &= ~EV_FLAG1;
+ }
+
+ /* XXX lock the proc here while adding to the list? */
+ SLIST_INSERT_HEAD(&p->p_klist, kn, kn_selnext);
+
+ return (0);
+}
+
+/*
+ * The knote may be attached to a different process, which may exit,
+ * leaving nothing for the knote to be attached to. So when the process
+ * exits, the knote is marked as DETACHED and also flagged as ONESHOT so
+ * it will be deleted when read out. However, as part of the knote deletion,
+ * this routine is called, so a check is needed to avoid actually performing
+ * a detach, because the original process does not exist any more.
+ */
+static void
+filt_procdetach(struct knote *kn)
+{
+ struct proc *p = kn->kn_ptr.p_proc;
+
+ if (kn->kn_status & KN_DETACHED)
+ return;
+
+ /* XXX locking? this might modify another process. */
+ SLIST_REMOVE(&p->p_klist, kn, knote, kn_selnext);
+}
+
+static int
+filt_proc(struct knote *kn, long hint)
+{
+ u_int event;
+
+ /*
+ * mask off extra data
+ */
+ event = (u_int)hint & NOTE_PCTRLMASK;
+
+ /*
+ * if the user is interested in this event, record it.
+ */
+ if (kn->kn_sfflags & event)
+ kn->kn_fflags |= event;
+
+ /*
+ * process is gone, so flag the event as finished.
+ */
+ if (event == NOTE_EXIT) {
+ kn->kn_status |= KN_DETACHED;
+ kn->kn_flags |= (EV_EOF | EV_ONESHOT);
+ return (1);
+ }
+
+ /*
+ * process forked, and user wants to track the new process,
+ * so attach a new knote to it, and immediately report an
+ * event with the parent's pid.
+ */
+ if ((event == NOTE_FORK) && (kn->kn_sfflags & NOTE_TRACK)) {
+ struct kevent kev;
+ int error;
+
+ /*
+ * register knote with new process.
+ */
+ kev.ident = hint & NOTE_PDATAMASK; /* pid */
+ kev.filter = kn->kn_filter;
+ kev.flags = kn->kn_flags | EV_ADD | EV_ENABLE | EV_FLAG1;
+ kev.fflags = kn->kn_sfflags;
+ kev.data = kn->kn_id; /* parent */
+ error = kqueue_register(kn->kn_kq, &kev, NULL);
+ if (error)
+ kn->kn_fflags |= NOTE_TRACKERR;
+ }
+
+ return (kn->kn_fflags != 0);
+}
+
+static int
+kqueue_create(struct kqueue **kqp)
+{
+ struct kqueue *kq;
+
+ kq = malloc(sizeof(struct kqueue), M_TEMP, M_WAITOK);
+ if (kq == NULL)
+ return (EAGAIN);
+ bzero(kq, sizeof(*kq));
+ TAILQ_INIT(&kq->kq_head);
+
+ *kqp = kq;
+ return (0);
+}
+
+int
+kqueue(struct proc *p, struct kqueue_args *uap)
+{
+ struct filedesc *fdp = p->p_fd;
+ struct kqueue *kq;
+ struct file *fp;
+ int fd, error;
+
+ error = falloc(p, &fp, &fd);
+ if (error)
+ return (error);
+ fp->f_flag = FREAD | FWRITE;
+ fp->f_type = DTYPE_KQUEUE;
+ fp->f_ops = &kqueueops;
+ error = kqueue_create(&kq);
+ if (error) {
+ fdp->fd_ofiles[fd] = 0;
+ ffree(fp);
+ } else {
+ fp->f_data = (caddr_t)kq;
+ p->p_retval[0] = fd;
+ }
+ fdp->fd_knlistsize = 0; /* mark this fdesc as having a kq */
+ kq->kq_fdp = fdp;
+ return (error);
+}
+
+#ifndef _SYS_SYSPROTO_H_
+struct kevent_args {
+ int fd;
+ int nchanges;
+ struct kevent **changelist;
+ int nevents;
+ struct kevent *eventlist;
+ struct timespec *timeout;
+};
+#endif
+int
+kevent(struct proc *p, struct kevent_args *uap)
+{
+ struct filedesc* fdp = p->p_fd;
+ struct kevent kev;
+ struct kqueue *kq;
+ struct file *fp;
+ struct timespec ts;
+ int i, n, nerrors, error;
+
+ if (((u_int)uap->fd) >= fdp->fd_nfiles ||
+ (fp = fdp->fd_ofiles[uap->fd]) == NULL ||
+ (fp->f_type != DTYPE_KQUEUE))
+ return (EBADF);
+
+ if (uap->timeout != NULL) {
+ error = copyin((caddr_t)uap->timeout, (caddr_t)&ts,
+ sizeof(ts));
+ if (error)
+ return error;
+ uap->timeout = &ts;
+ }
+
+ kq = (struct kqueue *)fp->f_data;
+ nerrors = 0;
+
+ while (uap->nchanges > 0) {
+ n = uap->nchanges > KQ_NEVENTS ? KQ_NEVENTS : uap->nchanges;
+ error = copyin((caddr_t)uap->changelist, (caddr_t)kq->kq_kevp,
+ n * sizeof(struct kevent *));
+ if (error)
+ return (error);
+ for (i = 0; i < n; i++) {
+ error = copyin((caddr_t)kq->kq_kevp[i],
+ (caddr_t)&kev, sizeof(kev));
+ if (error)
+ return (error);
+ kev.flags &= ~EV_SYSFLAGS;
+ error = kqueue_register(kq, &kev, p);
+ if (error) {
+ if (uap->nevents != 0) {
+ kev.flags = EV_ERROR;
+ kev.data = error;
+ (void) copyout((caddr_t)&kev,
+ (caddr_t)uap->eventlist,
+ sizeof(kev));
+ uap->eventlist++;
+ uap->nevents--;
+ nerrors++;
+ } else {
+ return (error);
+ }
+ }
+ }
+ uap->nchanges -= n;
+ uap->changelist += n;
+ }
+ if (nerrors) {
+ p->p_retval[0] = nerrors;
+ return (0);
+ }
+
+ error = kqueue_scan(fp, uap->nevents, uap->eventlist, uap->timeout, p);
+ return (error);
+}
+
+int
+kqueue_register(struct kqueue *kq, struct kevent *kev, struct proc *p)
+{
+ struct filedesc *fdp = kq->kq_fdp;
+ struct filterops *fops;
+ struct file *fp = NULL;
+ struct knote *kn = NULL;
+ int s, error = 0;
+
+ if (kev->filter < 0) {
+ if (kev->filter + EVFILT_SYSCOUNT < 0)
+ return (EINVAL);
+ fops = sysfilt_ops[~kev->filter]; /* to 0-base index */
+ } else {
+ /*
+ * XXX
+ * filter attach routine is responsible for insuring that
+ * the identifier can be attached to it.
+ */
+ printf("unknown filter: %d\n", kev->filter);
+ return (EINVAL);
+ }
+
+ if (fops->f_isfd) {
+ /* validate descriptor; ignore invalid descriptors */
+ if ((u_int)kev->ident >= fdp->fd_nfiles ||
+ (fp = fdp->fd_ofiles[kev->ident]) == NULL)
+ return (0);
+
+ if (kev->ident < fdp->fd_knlistsize) {
+ SLIST_FOREACH(kn, &fdp->fd_knlist[kev->ident], kn_link)
+ if (kq == kn->kn_kq &&
+ kev->filter == kn->kn_filter)
+ break;
+ }
+ } else {
+ if (fdp->fd_knhashmask != 0) {
+ struct klist *list;
+
+ list = &fdp->fd_knhash[
+ KN_HASH((u_long)kev->ident, fdp->fd_knhashmask)];
+ SLIST_FOREACH(kn, list, kn_link)
+ if (kev->ident == kn->kn_id &&
+ kq == kn->kn_kq &&
+ kev->filter == kn->kn_filter)
+ break;
+ }
+ }
+
+ if (kn == NULL && ((kev->flags & EV_ADD) == 0))
+ goto done;
+
+ /*
+ * kn now contains the matching knote, or NULL if no match
+ */
+ if (kev->flags & EV_ADD) {
+ int attach = 0;
+
+ if (kn == NULL) {
+ kn = knote_alloc();
+ if (kn == NULL)
+ return (ENOMEM);
+ if (fp != NULL)
+ fhold(fp);
+ kn->kn_fp = fp;
+ kn->kn_kq = kq;
+ kn->kn_fop = fops;
+ attach = 1;
+ }
+ kn->kn_sfflags = kev->fflags;
+ kn->kn_sdata = kev->data;
+ kev->fflags = 0;
+ kev->data = 0;
+ kn->kn_kevent = *kev;
+
+ if (attach) {
+ knote_attach(kn, fdp);
+ if ((error = fops->f_attach(kn)) != 0) {
+ knote_drop(kn, p);
+ goto done;
+ }
+ }
+ s = splhigh();
+ if (kn->kn_fop->f_event(kn, 0))
+ KNOTE_ACTIVATE(kn);
+ splx(s);
+ } else if (kev->flags & EV_DELETE) {
+ kn->kn_fop->f_detach(kn);
+ knote_drop(kn, p);
+ goto done;
+ }
+
+ if ((kev->flags & EV_DISABLE) &&
+ ((kn->kn_status & KN_DISABLED) == 0)) {
+ s = splhigh();
+ kn->kn_status |= KN_DISABLED;
+ splx(s);
+ }
+
+ if ((kev->flags & EV_ENABLE) && (kn->kn_status & KN_DISABLED)) {
+ s = splhigh();
+ kn->kn_status &= ~KN_DISABLED;
+ if ((kn->kn_status & KN_ACTIVE) &&
+ ((kn->kn_status & KN_QUEUED) == 0))
+ knote_enqueue(kn);
+ splx(s);
+ }
+
+done:
+ return (error);
+}
+
+static int
+kqueue_scan(struct file *fp, int maxevents, struct kevent *ulistp,
+ struct timespec *tsp, struct proc *p)
+{
+ struct kqueue *kq = (struct kqueue *)fp->f_data;
+ struct kevent *kevp;
+ struct timeval atv, rtv, ttv;
+ struct knote *kn, marker;
+ int s, count, timeout, nkev = 0, error = 0;
+
+ count = maxevents;
+ if (count == 0)
+ goto done;
+
+ if (tsp != NULL) {
+ TIMESPEC_TO_TIMEVAL(&atv, tsp);
+ if (itimerfix(&atv)) {
+ error = EINVAL;
+ goto done;
+ }
+ timeout = atv.tv_sec > 24 * 60 * 60 ?
+ 24 * 60 * 60 * hz : tvtohz(&atv);
+ getmicrouptime(&rtv);
+ timevaladd(&atv, &rtv);
+ } else {
+ atv.tv_sec = 0;
+ timeout = 0;
+ }
+ goto start;
+
+retry:
+ if (atv.tv_sec) {
+ getmicrouptime(&rtv);
+ if (timevalcmp(&rtv, &atv, >=))
+ goto done;
+ ttv = atv;
+ timevalsub(&ttv, &rtv);
+ timeout = ttv.tv_sec > 24 * 60 * 60 ?
+ 24 * 60 * 60 * hz : tvtohz(&ttv);
+ }
+
+start:
+ kevp = kq->kq_kev;
+ s = splhigh();
+ if (kq->kq_count == 0) {
+ kq->kq_state |= KQ_SLEEP;
+ error = tsleep(kq, PSOCK | PCATCH, "kqread", timeout);
+ splx(s);
+ if (error == 0)
+ goto retry;
+ /* don't restart after signals... */
+ if (error == ERESTART)
+ error = EINTR;
+ else if (error == EWOULDBLOCK)
+ error = 0;
+ goto done;
+ }
+
+ TAILQ_INSERT_TAIL(&kq->kq_head, &marker, kn_tqe);
+ while (count) {
+ kn = TAILQ_FIRST(&kq->kq_head);
+ TAILQ_REMOVE(&kq->kq_head, kn, kn_tqe);
+ if (kn == &marker) {
+ splx(s);
+ if (count == maxevents)
+ goto retry;
+ goto done;
+ }
+ if (kn->kn_status & KN_DISABLED) {
+ kn->kn_status &= ~KN_QUEUED;
+ kq->kq_count--;
+ continue;
+ }
+ if ((kn->kn_flags & EV_ONESHOT) == 0 &&
+ kn->kn_fop->f_event(kn, 0) == 0) {
+ kn->kn_status &= ~(KN_QUEUED | KN_ACTIVE);
+ kq->kq_count--;
+ continue;
+ }
+ *kevp = kn->kn_kevent;
+ kevp++;
+ nkev++;
+ if (kn->kn_flags & EV_ONESHOT) {
+ kn->kn_status &= ~KN_QUEUED;
+ kq->kq_count--;
+ splx(s);
+ kn->kn_fop->f_detach(kn);
+ knote_drop(kn, p);
+ s = splhigh();
+ } else if (kn->kn_flags & EV_CLEAR) {
+ kn->kn_data = 0;
+ kn->kn_fflags = 0;
+ kn->kn_status &= ~(KN_QUEUED | KN_ACTIVE);
+ kq->kq_count--;
+ } else {
+ TAILQ_INSERT_TAIL(&kq->kq_head, kn, kn_tqe);
+ }
+ count--;
+ if (nkev == KQ_NEVENTS) {
+ splx(s);
+ error = copyout((caddr_t)&kq->kq_kev, (caddr_t)ulistp,
+ sizeof(struct kevent) * nkev);
+ ulistp += nkev;
+ nkev = 0;
+ kevp = kq->kq_kev;
+ s = splhigh();
+ if (error)
+ break;
+ }
+ }
+ TAILQ_REMOVE(&kq->kq_head, &marker, kn_tqe);
+ splx(s);
+done:
+ if (nkev != 0)
+ error = copyout((caddr_t)&kq->kq_kev, (caddr_t)ulistp,
+ sizeof(struct kevent) * nkev);
+ p->p_retval[0] = maxevents - count;
+ return (error);
+}
+
+/*
+ * XXX
+ * This could be expanded to call kqueue_scan, if desired.
+ */
+/*ARGSUSED*/
+static int
+kqueue_read(struct file *fp, struct uio *uio, struct ucred *cred,
+ int flags, struct proc *p)
+{
+ return (ENXIO);
+}
+
+/*ARGSUSED*/
+static int
+kqueue_write(struct file *fp, struct uio *uio, struct ucred *cred,
+ int flags, struct proc *p)
+{
+ return (ENXIO);
+}
+
+/*ARGSUSED*/
+static int
+kqueue_ioctl(struct file *fp, u_long com, caddr_t data, struct proc *p)
+{
+ return (ENOTTY);
+}
+
+/*ARGSUSED*/
+static int
+kqueue_poll(struct file *fp, int events, struct ucred *cred, struct proc *p)
+{
+ struct kqueue *kq = (struct kqueue *)fp->f_data;
+ int revents = 0;
+ int s = splnet();
+
+ if (events & (POLLIN | POLLRDNORM)) {
+ if (kq->kq_count) {
+ revents |= events & (POLLIN | POLLRDNORM);
+ } else {
+ selrecord(p, &kq->kq_sel);
+ kq->kq_state |= KQ_SEL;
+ }
+ }
+ splx(s);
+ return (revents);
+}
+
+/*ARGSUSED*/
+static int
+kqueue_stat(struct file *fp, struct stat *st, struct proc *p)
+{
+ struct kqueue *kq = (struct kqueue *)fp->f_data;
+
+ bzero((void *)st, sizeof(*st));
+ st->st_size = kq->kq_count;
+ st->st_blksize = sizeof(struct kevent);
+ return (0);
+}
+
+/*ARGSUSED*/
+static int
+kqueue_close(struct file *fp, struct proc *p)
+{
+ struct kqueue *kq = (struct kqueue *)fp->f_data;
+ struct filedesc *fdp = p->p_fd;
+ struct knote **knp, *kn, *kn0;
+ int i;
+
+ for (i = 0; i < fdp->fd_knlistsize; i++) {
+ knp = &SLIST_FIRST(&fdp->fd_knlist[i]);
+ kn = *knp;
+ while (kn != NULL) {
+ kn0 = SLIST_NEXT(kn, kn_link);
+ if (kq == kn->kn_kq) {
+ kn->kn_fop->f_detach(kn);
+ fdrop(kn->kn_fp, p);
+ knote_free(kn);
+ *knp = kn0;
+ } else {
+ knp = &SLIST_NEXT(kn, kn_link);
+ }
+ kn = kn0;
+ }
+ }
+ if (fdp->fd_knhashmask != 0) {
+ for (i = 0; i < fdp->fd_knhashmask + 1; i++) {
+ knp = &SLIST_FIRST(&fdp->fd_knhash[i]);
+ kn = *knp;
+ while (kn != NULL) {
+ kn0 = SLIST_NEXT(kn, kn_link);
+ if (kq == kn->kn_kq) {
+ kn->kn_fop->f_detach(kn);
+ /* XXX non-fd release of kn->kn_ptr */
+ knote_free(kn);
+ *knp = kn0;
+ } else {
+ knp = &SLIST_NEXT(kn, kn_link);
+ }
+ kn = kn0;
+ }
+ }
+ }
+ free(kq, M_TEMP);
+ fp->f_data = NULL;
+
+ return (0);
+}
+
+static void
+kqueue_wakeup(struct kqueue *kq)
+{
+
+ if (kq->kq_state & KQ_SLEEP) {
+ kq->kq_state &= ~KQ_SLEEP;
+ wakeup(kq);
+ }
+ if (kq->kq_state & KQ_SEL) {
+ kq->kq_state &= ~KQ_SEL;
+ selwakeup(&kq->kq_sel);
+ }
+ KNOTE(&kq->kq_sel.si_note, 0);
+}
+
+/*
+ * walk down a list of knotes, activating them if their event has triggered.
+ */
+void
+knote(struct klist *list, long hint)
+{
+ struct knote *kn;
+
+ SLIST_FOREACH(kn, list, kn_selnext)
+ if (kn->kn_fop->f_event(kn, hint))
+ KNOTE_ACTIVATE(kn);
+}
+
+/*
+ * remove all knotes from a specified klist
+ */
+void
+knote_remove(struct proc *p, struct klist *list)
+{
+ struct knote *kn;
+
+ while ((kn = SLIST_FIRST(list)) != NULL) {
+ kn->kn_fop->f_detach(kn);
+ knote_drop(kn, p);
+ }
+}
+
+/*
+ * remove all knotes referencing a specified fd
+ */
+void
+knote_fdclose(struct proc *p, int fd)
+{
+ struct filedesc *fdp = p->p_fd;
+ struct klist *list = &fdp->fd_knlist[fd];
+
+ knote_remove(p, list);
+}
+
+static void
+knote_attach(struct knote *kn, struct filedesc *fdp)
+{
+ struct klist *list;
+ int size;
+
+ if (! kn->kn_fop->f_isfd) {
+ if (fdp->fd_knhashmask == 0)
+ fdp->fd_knhash = hashinit(KN_HASHSIZE, M_TEMP,
+ &fdp->fd_knhashmask);
+ list = &fdp->fd_knhash[KN_HASH(kn->kn_id, fdp->fd_knhashmask)];
+ goto done;
+ }
+
+ if (fdp->fd_knlistsize <= kn->kn_id) {
+ size = fdp->fd_knlistsize;
+ while (size <= kn->kn_id)
+ size += KQEXTENT;
+ MALLOC(list, struct klist *,
+ size * sizeof(struct klist *), M_TEMP, M_WAITOK);
+ bcopy((caddr_t)fdp->fd_knlist, (caddr_t)list,
+ fdp->fd_knlistsize * sizeof(struct klist *));
+ bzero((caddr_t)list +
+ fdp->fd_knlistsize * sizeof(struct klist *),
+ (size - fdp->fd_knlistsize) * sizeof(struct klist *));
+ if (fdp->fd_knlist != NULL)
+ FREE(fdp->fd_knlist, M_TEMP);
+ fdp->fd_knlistsize = size;
+ fdp->fd_knlist = list;
+ }
+ list = &fdp->fd_knlist[kn->kn_id];
+done:
+ SLIST_INSERT_HEAD(list, kn, kn_link);
+ kn->kn_status = 0;
+}
+
+/*
+ * should be called at spl == 0, since we don't want to hold spl
+ * while calling fdrop and free.
+ */
+static void
+knote_drop(struct knote *kn, struct proc *p)
+{
+ struct filedesc *fdp = p->p_fd;
+ struct klist *list;
+
+ if (kn->kn_fop->f_isfd)
+ list = &fdp->fd_knlist[kn->kn_id];
+ else
+ list = &fdp->fd_knhash[KN_HASH(kn->kn_id, fdp->fd_knhashmask)];
+
+ SLIST_REMOVE(list, kn, knote, kn_link);
+ if (kn->kn_status & KN_QUEUED)
+ knote_dequeue(kn);
+ if (kn->kn_fop->f_isfd)
+ fdrop(kn->kn_fp, p);
+ knote_free(kn);
+}
+
+
+static void
+knote_enqueue(struct knote *kn)
+{
+ struct kqueue *kq = kn->kn_kq;
+ int s = splhigh();
+
+ KASSERT((kn->kn_status & KN_QUEUED) == 0, ("knote already queued"));
+
+ TAILQ_INSERT_TAIL(&kq->kq_head, kn, kn_tqe);
+ kn->kn_status |= KN_QUEUED;
+ kq->kq_count++;
+ splx(s);
+ kqueue_wakeup(kq);
+}
+
+static void
+knote_dequeue(struct knote *kn)
+{
+ struct kqueue *kq = kn->kn_kq;
+ int s = splhigh();
+
+ KASSERT(kn->kn_status & KN_QUEUED, ("knote not queued"));
+
+ TAILQ_REMOVE(&kq->kq_head, kn, kn_tqe);
+ kn->kn_status &= ~KN_QUEUED;
+ kq->kq_count--;
+ splx(s);
+}
+
+static void
+knote_init(void)
+{
+ knote_zone = zinit("KNOTE", sizeof(struct knote), 0, 0, 1);
+}
+SYSINIT(knote, SI_SUB_PSEUDO, SI_ORDER_ANY, knote_init, NULL)
+
+static struct knote *
+knote_alloc(void)
+{
+ return ((struct knote *)zalloc(knote_zone));
+}
+
+static void
+knote_free(struct knote *kn)
+{
+ zfree(knote_zone, kn);
+}
diff --git a/sys/sys/event.h b/sys/sys/event.h
new file mode 100644
index 000000000000..366b3476f81b
--- /dev/null
+++ b/sys/sys/event.h
@@ -0,0 +1,165 @@
+/*-
+ * Copyright (c) 1999,2000 Jonathan Lemon <jlemon@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _SYS_EVENT_H_
+#define _SYS_EVENT_H_
+
+#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_SYSCOUNT 6
+
+struct kevent {
+ uintptr_t ident; /* identifier for this event */
+ short filter; /* filter for event */
+ u_short flags;
+ u_int fflags;
+ intptr_t data;
+ void *udata; /* opaque user data identifier */
+};
+
+/* actions */
+#define EV_ADD 0x0001 /* add event to kq (implies enable) */
+#define EV_DELETE 0x0002 /* delete event from kq */
+#define EV_ENABLE 0x0004 /* enable event */
+#define EV_DISABLE 0x0008 /* disable event (not reported) */
+
+/* flags */
+#define EV_ONESHOT 0x0010 /* only report one occurrence */
+#define EV_CLEAR 0x0020 /* clear event state after reporting */
+
+#define EV_SYSFLAGS 0xF000 /* reserved by system */
+#define EV_FLAG1 0x2000 /* filter-specific flag */
+
+/* returned values */
+#define EV_EOF 0x8000 /* EOF detected */
+#define EV_ERROR 0x4000 /* error, data contains errno */
+
+/*
+ * data/hint flags for EVFILT_VNODE, shared with userspace
+ */
+#define NOTE_DELETE 0x0001 /* vnode was removed */
+#define NOTE_WRITE 0x0002 /* data contents changed */
+#define NOTE_EXTEND 0x0004 /* size increased */
+#define NOTE_ATTRIB 0x0008 /* attributes changed */
+#define NOTE_LINK 0x0010 /* link count changed */
+#define NOTE_RENAME 0x0020 /* vnode was renamed */
+
+/*
+ * data/hint flags for EVFILT_PROC, shared with userspace
+ */
+#define NOTE_EXIT 0x80000000 /* process exited */
+#define NOTE_FORK 0x40000000 /* process forked */
+#define NOTE_EXEC 0x20000000 /* process exec'd */
+#define NOTE_PCTRLMASK 0xf0000000 /* mask for hint bits */
+#define NOTE_PDATAMASK 0x000fffff /* mask for pid */
+
+/* additional flags for EVFILT_PROC */
+#define NOTE_TRACK 0x00000001 /* follow across forks */
+#define NOTE_TRACKERR 0x00000002 /* could not track child */
+#define NOTE_CHILD 0x00000004 /* am a child process */
+
+/*
+ * This is currently visible to userland to work around broken
+ * programs which pull in <sys/proc.h> or <sys/select.h>.
+ */
+#include <sys/queue.h>
+struct knote;
+SLIST_HEAD(klist, knote);
+
+#ifdef _KERNEL
+
+#define KNOTE(list, hint) if ((list) != NULL) knote(list, hint)
+
+/*
+ * Flag indicating hint is a signal. Used by EVFILT_SIGNAL, and also
+ * shared by EVFILT_PROC (all knotes attached to p->p_klist)
+ */
+#define NOTE_SIGNAL 0x08000000
+
+struct filterops {
+ int f_isfd; /* true if ident == filedescriptor */
+ int (*f_attach) __P((struct knote *kn));
+ void (*f_detach) __P((struct knote *kn));
+ int (*f_event) __P((struct knote *kn, long hint));
+};
+
+struct knote {
+ SLIST_ENTRY(knote) kn_link; /* for fd */
+ SLIST_ENTRY(knote) kn_selnext; /* for struct selinfo */
+ TAILQ_ENTRY(knote) kn_tqe;
+ struct kqueue *kn_kq; /* which queue we are on */
+ struct kevent kn_kevent;
+ int kn_status;
+ int kn_sfflags; /* saved filter flags */
+ intptr_t kn_sdata; /* saved data field */
+ union {
+ struct file *p_fp; /* file data pointer */
+ struct proc *p_proc; /* proc pointer */
+ } kn_ptr;
+ struct filterops *kn_fop;
+#define KN_ACTIVE 0x01 /* event has been triggered */
+#define KN_QUEUED 0x02 /* event is on queue */
+#define KN_DISABLED 0x04 /* event is disabled */
+#define KN_DETACHED 0x08 /* knote is detached */
+
+#define kn_id kn_kevent.ident
+#define kn_filter kn_kevent.filter
+#define kn_flags kn_kevent.flags
+#define kn_fflags kn_kevent.fflags
+#define kn_data kn_kevent.data
+#define kn_fp kn_ptr.p_fp
+};
+
+struct proc;
+
+extern void knote(struct klist *list, long hint);
+extern void knote_remove(struct proc *p, struct klist *list);
+extern void knote_fdclose(struct proc *p, int fd);
+extern int kqueue_register(struct kqueue *kq,
+ struct kevent *kev, struct proc *p);
+
+#else /* !_KERNEL */
+
+#include <sys/cdefs.h>
+struct timespec;
+
+__BEGIN_DECLS
+int kqueue __P((void));
+int kevent __P((int kq, int nchanges, struct kevent **changelist,
+ int nevents, struct kevent *eventlist,
+ struct timespec *timeout));
+__END_DECLS
+
+#endif /* !_KERNEL */
+
+#endif /* !_SYS_EVENT_H_ */