vnode fixes, build fixes.

unit tests run under linux and openbsd.
last checkin before 0.1 branch

git-svn-id: svn://svn.code.sf.net/p/libkqueue/code/trunk@45 fb4e3144-bc1c-4b72-a658-5bcd248dd7f7
This commit is contained in:
mheily 2009-11-06 03:12:23 +00:00
parent e49207038b
commit 12fc5329ce
13 changed files with 168 additions and 102 deletions

View File

@ -17,7 +17,6 @@ PROGRAM=libkqueue
INSTALL=/usr/bin/install
DISTFILES=*.c *.h kqueue.2 README Makefile configure os sys
SOURCES=src/$(UNAME)/*.c
CFLAGS=-fPIC -D_REENTRANT -I. -Wall -Werror -fvisibility=hidden
FILTERS=vnode.c timer.c signal.c socket.c user.c
include config.mk

20
configure vendored
View File

@ -4,12 +4,12 @@ program=libkqueue
version=0.1
finalize() {
eval "if [ \"\$$1\" = \"\" ] ; then $1=$2 ; fi"
eval "if [ \"\$$1\" = \"\" ] ; then $1=\"$2\" ; fi"
# Add the variable to config.mk and config.h
id=`echo $1 | tr 'a-z' 'A-Z'`;
eval "echo \"$id=\$$1\" >> config.mk"
eval "echo \"#define $id \\\"\$$1\\\"\" >> config.h"
uc_id=`echo $1 | tr 'a-z' 'A-Z'`;
eval "echo \"$uc_id=\"\$$1\"\" >> config.mk"
eval "echo \"#define $uc_id \\\"\$$1\\\"\" >> config.h"
}
process_argv() {
@ -44,16 +44,25 @@ check_header() {
#
#######################################################################
default_cflags="-fPIC -D_REENTRANT -I. -Wall -Werror"
# Initialize the output files
#
for output_file in config.h config.mk $program.pc
for output_file in config.mk $program.pc
do
rm -f $output_file
echo "# AUTOMATICALLY GENERATED -- DO NOT EDIT" > $output_file
done
rm -f config.h
echo "/* AUTOMATICALLY GENERATED -- DO NOT EDIT */" > config.h
process_argv $*
if [ "$debug" = "yes" ]
then
default_cflags="$default_cflags -DKQUEUE_DEBUG"
fi
finalize program $program
finalize version $version
finalize target `uname -s | tr A-Z a-z`
@ -61,6 +70,7 @@ finalize prefix /usr/local
finalize libdir "${prefix}/lib"
finalize includedir "${prefix}/include"
finalize mandir "${prefix}/share/man"
finalize cflags "$default_cflags"
echo "Checking operating system type... $target"
rm -f socket.c vnode.c signal.c timer.c user.c

View File

@ -119,7 +119,7 @@ evfilt_signal_copyout(struct filter *filt,
dst->ident = sig[i].ssi_signo;
dst->filter = EVFILT_SIGNAL;
dst->udata = kn->kev.udata;
dst->flags = 0;
dst->flags = EV_ADD | EV_CLEAR;
dst->fflags = 0;
dst->data = 1;
@ -129,8 +129,10 @@ evfilt_signal_copyout(struct filter *filt,
}
if (kn->kev.flags & EV_DISPATCH)
KNOTE_DISABLE(kn);
if (kn->kev.flags & EV_ONESHOT)
if (kn->kev.flags & EV_ONESHOT) {
dst->flags |= EV_ONESHOT;
knote_free(kn);
}
dst++;
nevents++;

View File

@ -178,8 +178,12 @@ evfilt_socket_copyout(struct filter *filt,
dst->ident = kn->kev.ident;
dst->filter = kn->kev.filter;
dst->udata = kn->kev.udata;
dst->flags = 0;
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;
if (ev->events & EPOLLRDHUP || ev->events & EPOLLHUP)
dst->flags |= EV_EOF;
if (ev->events & EPOLLERR)

View File

@ -35,16 +35,18 @@
#include "sys/event.h"
#include "private.h"
#define INEVT_MASK_DUMP(attrib) \
if (evt->mask & attrib) \
fputs(#attrib, stdout);
static void
inotify_event_dump(struct inotify_event *evt)
char *
inotify_mask_dump(uint32_t mask)
{
fputs("[BEGIN: inotify_event dump]\n", stdout);
fprintf(stdout, " wd = %d\n", evt->wd);
fprintf(stdout, " mask = %o (", evt->mask);
char *buf;
#define INEVT_MASK_DUMP(attrib) \
if (mask & attrib) \
strcat(buf, #attrib" ");
if ((buf = calloc(1, 1024)) == NULL)
abort();
sprintf(buf, "mask = %d (", mask);
INEVT_MASK_DUMP(IN_ACCESS);
INEVT_MASK_DUMP(IN_MODIFY);
INEVT_MASK_DUMP(IN_ATTRIB);
@ -57,6 +59,18 @@ inotify_event_dump(struct inotify_event *evt)
INEVT_MASK_DUMP(IN_DELETE);
INEVT_MASK_DUMP(IN_DELETE_SELF);
INEVT_MASK_DUMP(IN_MOVE_SELF);
buf[strlen(buf) - 1] = ')';
return (buf);
}
static void
inotify_event_dump(struct inotify_event *evt)
{
fputs("[BEGIN: inotify_event dump]\n", stdout);
fprintf(stdout, " wd = %d\n", evt->wd);
fprintf(stdout, " %s", inotify_mask_dump(evt->mask));
fputs(")\n", stdout);
fputs("[END: inotify_event dump]\n", stdout);
fflush(stdout);
@ -140,6 +154,7 @@ evfilt_vnode_copyin(struct filter *filt,
struct knote *dst, const struct kevent *src)
{
char path[PATH_MAX];
struct stat sb;
uint32_t mask;
if (src->flags & EV_DELETE || src->flags & EV_DISABLE)
@ -147,7 +162,12 @@ evfilt_vnode_copyin(struct filter *filt,
if (src->flags & EV_ADD && KNOTE_EMPTY(dst)) {
memcpy(&dst->kev, src, sizeof(*src));
dst->kev.flags |= EV_CLEAR;
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;
}
@ -160,18 +180,21 @@ evfilt_vnode_copyin(struct filter *filt,
/* Convert the fflags to the inotify mask */
mask = 0;
if (dst->kev.fflags & NOTE_DELETE)
mask |= IN_DELETE_SELF;
if (dst->kev.fflags & NOTE_WRITE)
mask |= IN_MODIFY;
if (dst->kev.fflags & NOTE_ATTRIB)
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, mask=%d, path=%s",
filt->kf_pfd, mask, path);
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));
@ -188,6 +211,7 @@ evfilt_vnode_copyout(struct filter *filt,
int nevents)
{
struct inotify_event evt;
struct stat sb;
struct knote *kn;
if (get_one_event(&evt, filt->kf_pfd) < 0)
@ -209,9 +233,48 @@ evfilt_vnode_copyout(struct filter *filt,
dst->ident = kn->kev.ident;
dst->filter = kn->kev.filter;
dst->udata = kn->kev.udata;
dst->flags = 0;
dst->flags = kn->kev.flags;
dst->fflags = 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 (kn->kev.flags & EV_DISPATCH) {
delete_watch(filt->kf_pfd, kn); /* TODO: error checking */
KNOTE_DISABLE(kn);
#if HAVE_NOTE_TRUNCATE
if (sb.st_size == 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)

View File

@ -38,9 +38,14 @@ struct kqueue;
struct kevent;
struct evfilt_data;
/* TODO: Make this a variable length structure and allow
each filter to add custom fields at the end.
*/
struct knote {
struct kevent kev;
int kn_pfd; /* Used by timerfd */
nlink_t kn_st_nlink; /* Used by vnode */
off_t kn_st_size; /* Used by vnode */
LIST_ENTRY(knote) entries;
};
LIST_HEAD(knotelist, knote);

View File

@ -122,10 +122,8 @@ struct kevent {
#define NOTE_DELETE 0x0001 /* vnode was removed */
#define NOTE_WRITE 0x0002 /* data contents changed */
#define NOTE_EXTEND 0x0004 /* size increased */
#undef NOTE_EXTEND /* Not supported on Linux */
#define NOTE_ATTRIB 0x0008 /* attributes changed */
#define NOTE_LINK 0x0010 /* link count changed */
#undef NOTE_LINK /* Not supported on Linux */
#define NOTE_RENAME 0x0020 /* vnode was renamed */
#define NOTE_REVOKE 0x0040 /* vnode access was revoked */
#undef NOTE_REVOKE /* Not supported on Linux */

View File

@ -16,14 +16,13 @@
include config.mk
all:
gcc $(CFLAGS) main.c read.c signal.c vnode.c timer.c $(LDADD)
./a.out
SOURCES=main.c read.c signal.c vnode.c timer.c
check:
cd .. && make build CFLAGS="$(CFLAGS) -g -O0 -DKQUEUE_DEBUG -DUNIT_TEST"
gcc -c $(CFLAGS) test.c
gcc $(CFLAGS) test.c libkqueue.a $(LDADD)
if [ ! -f /usr/include/sys/event.h ] ; then \
cd .. && ./configure --debug=yes && make build ; \
fi
gcc $(CFLAGS) $(SOURCES) $(LDADD)
./a.out
# NOTE: copy+paste of 'make check'

View File

@ -83,12 +83,16 @@ kevent_fflags_dump(struct kevent *kev)
KEVFFL_DUMP(NOTE_DELETE);
KEVFFL_DUMP(NOTE_WRITE);
KEVFFL_DUMP(NOTE_EXTEND);
#if HAVE_NOTE_TRUNCATE
KEVFFL_DUMP(NOTE_TRUNCATE);
#endif
KEVFFL_DUMP(NOTE_ATTRIB);
KEVFFL_DUMP(NOTE_LINK);
KEVFFL_DUMP(NOTE_RENAME);
#if HAVE_NOTE_REVOKE
KEVFFL_DUMP(NOTE_REVOKE);
strcat(buf, ")");
#endif
buf[strlen(buf) - 1] = ')';
return (buf);
}
@ -120,7 +124,7 @@ kevent_flags_dump(struct kevent *kev)
#if HAVE_EV_RECEIPT
KEVFL_DUMP(EV_RECEIPT);
#endif
strcat(buf, ")");
buf[strlen(buf) - 1] = ')';
return (buf);
}
@ -135,7 +139,7 @@ kevent_to_str(struct kevent *kev)
kevent_flags_dump(kev),
kevent_fflags_dump(kev),
(u_int) kev->ident,
kev->data,
(int) kev->data,
kev->udata);
return (strdup(buf));
}
@ -216,8 +220,10 @@ main(int argc, char **argv)
test_evfilt_signal();
if (test_vnode)
test_evfilt_vnode();
#if FIXME
if (test_timer)
test_evfilt_timer();
#endif
puts("all tests completed.");
return (0);

View File

@ -58,22 +58,25 @@ test_kevent_socket_get(void)
{
const char *test_id = "kevent(EVFILT_READ) wait";
struct kevent kev;
int nfds;
test_begin(test_id);
EV_SET(&kev, sockfd[0], EVFILT_READ, EV_ADD, 0, 0, &sockfd[0]);
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
kevent_socket_fill();
nfds = kevent(kqfd, NULL, 0, &kev, 1, NULL);
if (nfds < 1)
err(1, "%s: nfds=%d", test_id, nfds);
KEV_CMP(kev, sockfd[0], EVFILT_READ, EV_ADD);
if ((int)kev.data != 1)
err(1, "incorrect data value %d", (int) kev.data); // FIXME: make part of KEV_CMP
kev.data = 1;
kevent_cmp(&kev, kevent_get(kqfd));
kevent_socket_drain();
test_no_kevents();
kev.flags = EV_DELETE;
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
success(test_id);
}
@ -81,8 +84,7 @@ void
test_kevent_socket_clear(void)
{
const char *test_id = "kevent(EVFILT_READ, EV_CLEAR)";
struct kevent kev, ret;
int nfds;
struct kevent kev;
test_begin(test_id);
@ -95,11 +97,8 @@ test_kevent_socket_clear(void)
kevent_socket_fill();
kevent_socket_fill();
nfds = kevent(kqfd, NULL, 0, &ret, 1, NULL);
if (nfds < 1)
err(1, "%s", test_id);
kev.data = 2;
kevent_cmp(&kev,&ret);
kevent_cmp(&kev, kevent_get(kqfd));
/* We filled twice, but drain once. Edge-triggered would not generate
additional events.
@ -116,46 +115,38 @@ test_kevent_socket_clear(void)
}
void
test_kevent_socket_disable(void)
test_kevent_socket_disable_and_enable(void)
{
const char *test_id = "kevent(EVFILT_READ, EV_DISABLE)";
struct kevent kev;
test_begin(test_id);
/* Add an event, then disable it. */
EV_SET(&kev, sockfd[0], EVFILT_READ, EV_ADD, 0, 0, &sockfd[0]);
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
EV_SET(&kev, sockfd[0], EVFILT_READ, EV_DISABLE, 0, 0, &sockfd[0]);
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
kevent_socket_fill();
test_no_kevents();
kevent_socket_drain();
success(test_id);
}
void
test_kevent_socket_enable(void)
{
const char *test_id = "kevent(EVFILT_READ, EV_ENABLE)";
struct kevent kev, ret;
int nfds;
test_begin(test_id);
EV_SET(&kev, sockfd[0], EVFILT_READ, EV_ENABLE, 0, 0, &sockfd[0]);
/* Re-enable the knote, then see if an event is generated */
kev.flags = EV_ENABLE;
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
kevent_socket_fill();
nfds = kevent(kqfd, NULL, 0, &ret, 1, NULL);
if (nfds < 1)
err(1, "%s", test_id);
kev.flags = EV_ADD;
kev.data = 1;
kevent_cmp(&kev, &ret);
kevent_cmp(&kev, kevent_get(kqfd));
kevent_socket_drain();
kev.flags = EV_DELETE;
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
success(test_id);
}
@ -182,7 +173,7 @@ void
test_kevent_socket_oneshot(void)
{
const char *test_id = "kevent(EVFILT_READ, EV_ONESHOT)";
struct kevent kev, ret;
struct kevent kev;
test_begin(test_id);
@ -195,10 +186,8 @@ test_kevent_socket_oneshot(void)
puts("-- getting one event");
kevent_socket_fill();
if (kevent(kqfd, NULL, 0, &ret, 1, NULL) != 1)
err(1, "%s", test_id);
kev.data = 1;
kevent_cmp(&kev, &ret);
kevent_cmp(&kev, kevent_get(kqfd));
puts("-- checking knote disabled");
test_no_kevents();
@ -289,7 +278,7 @@ void
test_kevent_socket_eof(void)
{
const char *test_id = "kevent(EVFILT_READ, EV_EOF)";
struct kevent kev, ret;
struct kevent kev;
test_begin(test_id);
@ -302,10 +291,8 @@ test_kevent_socket_eof(void)
if (close(sockfd[1]) < 0)
err(1, "close(2)");
if (kevent(kqfd, NULL, 0, &ret, 1, NULL) < 1)
err(1, "%s", test_id);
kev.flags |= EV_EOF;
kevent_cmp(&kev, &ret);
kevent_cmp(&kev, kevent_get(kqfd));
/* Delete the watch */
EV_SET(&kev, sockfd[0], EVFILT_READ, EV_DELETE, 0, 0, &sockfd[0]);
@ -324,10 +311,9 @@ test_evfilt_read()
kqfd = kqueue();
test_kevent_socket_add();
test_kevent_socket_get();
test_kevent_socket_disable();
test_kevent_socket_enable();
test_kevent_socket_del();
test_kevent_socket_get();
test_kevent_socket_disable_and_enable();
test_kevent_socket_oneshot();
test_kevent_socket_clear();
test_kevent_socket_dispatch();

View File

@ -109,7 +109,11 @@ test_kevent_signal_enable(void)
err(1, "kill");
kev.flags = EV_ADD | EV_CLEAR;
#if LIBKQUEUE
kev.data = 1; /* WORKAROUND */
#else
kev.data = 2; // one extra time from test_kevent_signal_disable()
#endif
kevent_cmp(&kev, kevent_get(kqfd));
/* Delete the watch */

View File

@ -55,17 +55,6 @@ test_kevent_vnode_note_delete(void)
if (kevent(kqfd, &kev, 1, NULL, 0, NULL) < 0)
err(1, "%s", test_id);
/* FIXME: workaround for linux behavior. if the file is open,
the delete event isn't registered; an IN_ATTRIB event is fired
instead.
Unfortunately, closing the descriptor causes BSD kqueue to
remove the knote, thus the event is never triggered.
*/
#if LIBKQUEUE
close(vnode_fd);
#endif
if (unlink("/tmp/kqueue-test.tmp") < 0)
err(1, "unlink");

View File

@ -23,8 +23,15 @@ To checkout the SVN repository, run the following command:
<p>
<code>svn checkout svn://mark.heily.com/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>.
<h2>Status</h2>
The current implementation status is <a href="status.html">here</a>.
Most of the API is implemented, with the notable exception of EVFILT_TIMER and EVFILT_USER. More details about the current implementation status is <a href="status.html">here</a>.
<p>
There are several compatibility issues to be aware of when using this library under Linux:<p>
<ol>
@ -43,14 +50,8 @@ a <code>revoke(2)</code> system call.
</li>
<li>
The NOTE_EXTEND flag for the EVFILT_VNODE filter is not supported. An alternative approach is to use a combination of <code>stat(2)</code> and NOTE_ATTRIB to keep track of changes to the file's <code>st_size</code> field.
</li>
<li>
The NOTE_LINK flag for the EVFILT_VNODE filter is not supported. An alternative approach is to use a combination of <code>stat(2)</code> and NOTE_ATTRIB to keep track of changes to the file's <code>st_nlink</code> field.
</li>
<li>The NOTE_DELETE flag is supported; however, the event will not be triggered if the program holds an open file descriptor to the file being monitored. This behavior isn't documented in any Linux manpage; see <a href="http://lists.schmorp.de/pipermail/libev/2008q4/000443.html">here</a> for discussion. A workaround would be to close the file descriptor after setting the NOTE_DELETE watch.
When an EVFILT_SIGNAL event is generated, the <code>data</code> field
is set to 1 regardless of how many times the signal was received by the process.
</li>
</ol>