367 lines
9.0 KiB
C
367 lines
9.0 KiB
C
/*
|
|
* 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 "common.h"
|
|
|
|
extern int kqfd;
|
|
|
|
/* Checks if any events are pending, which is an error. */
|
|
void
|
|
_test_no_kevents(int kqfd, const char *file, int line)
|
|
{
|
|
int nfds;
|
|
struct timespec timeo;
|
|
struct kevent kev;
|
|
|
|
memset(&timeo, 0, sizeof(timeo));
|
|
nfds = kevent(kqfd, NULL, 0, &kev, 1, &timeo);
|
|
if (nfds < 0)
|
|
err(1, "kevent(2)");
|
|
if (nfds > 0) {
|
|
printf("\n[%s:%d]: Unexpected event:", file, line);
|
|
err(1, "%s", kevent_to_str(&kev));
|
|
}
|
|
}
|
|
|
|
/* Retrieve a single kevent */
|
|
void
|
|
kevent_get(struct kevent kev[], int numevents, int kqfd, int expect)
|
|
{
|
|
struct kevent buf;
|
|
int nfds;
|
|
|
|
if (kev == NULL) {
|
|
kev = &buf;
|
|
numevents = 1;
|
|
}
|
|
|
|
kevent_rv_cmp(expect, kevent(kqfd, NULL, 0, kev, numevents, NULL));
|
|
}
|
|
|
|
/**
|
|
* Retrieve a single kevent, specifying a maximum time to wait for it.
|
|
* Result:
|
|
* 0 = Timeout
|
|
* 1 = Success
|
|
*/
|
|
int
|
|
kevent_get_timeout(struct kevent kev[], int numevents, int fd, struct timespec *ts)
|
|
{
|
|
int nfds;
|
|
|
|
nfds = kevent(fd, NULL, 0, kev, numevents, ts);
|
|
if (nfds < 0) {
|
|
err(1, "kevent(2)");
|
|
} else if (nfds == 0) {
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* In Linux, a kevent() call with less than 1ms resolution
|
|
will perform a pselect() call to obtain the higer resolution.
|
|
This test exercises that codepath.
|
|
*/
|
|
void
|
|
kevent_get_hires(struct kevent kev[], int numevents, int kqfd, struct timespec *ts)
|
|
{
|
|
int nfds;
|
|
|
|
nfds = kevent(kqfd, NULL, 0, kev, numevents, ts);
|
|
if (nfds < 1)
|
|
die("kevent(2)");
|
|
}
|
|
|
|
static const char *
|
|
filter_name(short filt)
|
|
{
|
|
int id;
|
|
const char *fname[EVFILT_SYSCOUNT] = {
|
|
[~EVFILT_READ] = "EVFILT_READ",
|
|
[~EVFILT_WRITE] = "EVFILT_WRITE",
|
|
[~EVFILT_AIO] = "EVFILT_AIO",
|
|
[~EVFILT_VNODE] = "EVFILT_VNODE",
|
|
[~EVFILT_PROC] = "EVFILT_PROC",
|
|
[~EVFILT_SIGNAL] = "EVFILT_SIGNAL",
|
|
[~EVFILT_TIMER] = "EVFILT_TIMER",
|
|
#ifdef EVFILT_NETDEV
|
|
[~EVFILT_NETDEV] = "EVFILT_NETDEV",
|
|
#endif
|
|
[~EVFILT_FS] = "EVFILT_FS",
|
|
#ifdef EVFILT_LIO
|
|
[~EVFILT_LIO] = "EVFILT_LIO",
|
|
#endif
|
|
[~EVFILT_USER] = "EVFILT_USER",
|
|
|
|
#ifdef EVFILT_LIBKQUEUE
|
|
[~EVFILT_LIBKQUEUE] = "EVFILT_LIBKQUEUE"
|
|
#endif
|
|
};
|
|
|
|
id = ~filt;
|
|
if (id < 0 || id >= NUM_ELEMENTS(fname))
|
|
return "EVFILT_INVALID";
|
|
else
|
|
return fname[id];
|
|
}
|
|
|
|
static const char *
|
|
kevent_filter_dump(const struct kevent *kev)
|
|
{
|
|
static __thread char buf[64];
|
|
|
|
snprintf(buf, sizeof(buf), "%d (%s)",
|
|
kev->filter, filter_name(kev->filter));
|
|
return ((const char *) buf);
|
|
}
|
|
|
|
char *
|
|
kevent_fflags_dump(struct kevent *kev)
|
|
{
|
|
static __thread char buf[512];
|
|
size_t len;
|
|
|
|
#define KEVFFL_DUMP(attrib) \
|
|
if (kev->fflags & attrib) \
|
|
strncat((char *) buf, #attrib" ", 64);
|
|
|
|
snprintf(buf, sizeof(buf), "fflags=0x%04x (", kev->fflags);
|
|
switch (kev->filter) {
|
|
case 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);
|
|
break;
|
|
|
|
case EVFILT_USER:
|
|
KEVFFL_DUMP(NOTE_FFNOP);
|
|
KEVFFL_DUMP(NOTE_FFAND);
|
|
KEVFFL_DUMP(NOTE_FFOR);
|
|
KEVFFL_DUMP(NOTE_FFCOPY);
|
|
KEVFFL_DUMP(NOTE_TRIGGER);
|
|
break;
|
|
|
|
case EVFILT_READ:
|
|
case EVFILT_WRITE:
|
|
#ifdef NOTE_LOWAT
|
|
KEVFFL_DUMP(NOTE_LOWAT);
|
|
#endif
|
|
break;
|
|
|
|
case EVFILT_PROC:
|
|
KEVFFL_DUMP(NOTE_CHILD);
|
|
KEVFFL_DUMP(NOTE_EXIT);
|
|
#ifdef NOTE_EXITSTATUS
|
|
KEVFFL_DUMP(NOTE_EXITSTATUS);
|
|
#endif
|
|
KEVFFL_DUMP(NOTE_FORK);
|
|
KEVFFL_DUMP(NOTE_EXEC);
|
|
#ifdef NOTE_SIGNAL
|
|
KEVFFL_DUMP(NOTE_SIGNAL);
|
|
#endif
|
|
break;
|
|
|
|
#ifdef EVFILT_LIBKQUEUE
|
|
case EVFILT_LIBKQUEUE:
|
|
KEVFFL_DUMP(NOTE_VERSION);
|
|
KEVFFL_DUMP(NOTE_VERSION_STR);
|
|
break;
|
|
#endif
|
|
|
|
default:
|
|
break;
|
|
}
|
|
len = strlen(buf);
|
|
if (buf[len - 1] == ' ') buf[len - 1] = '\0'; /* Trim trailing space */
|
|
strcat(buf, ")");
|
|
|
|
#undef KEVFFL_DUMP
|
|
|
|
return (buf);
|
|
}
|
|
|
|
char *
|
|
kevent_flags_dump(struct kevent *kev)
|
|
{
|
|
static __thread char buf[512];
|
|
|
|
#define KEVFL_DUMP(attrib) \
|
|
if (kev->flags & attrib) \
|
|
strncat(buf, #attrib" ", 64);
|
|
|
|
snprintf(buf, sizeof(buf), "flags = %d (", 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);
|
|
#ifdef EV_DISPATCH
|
|
KEVFL_DUMP(EV_DISPATCH);
|
|
#endif
|
|
#ifdef EV_RECEIPT
|
|
KEVFL_DUMP(EV_RECEIPT);
|
|
#endif
|
|
buf[strlen(buf) - 1] = ')';
|
|
|
|
return (buf);
|
|
}
|
|
|
|
/* TODO - backport changes from src/common/kevent.c kevent_dump() */
|
|
const char *
|
|
kevent_to_str(struct kevent *kev)
|
|
{
|
|
static __thread char buf[512];
|
|
|
|
snprintf(buf, 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 buf;
|
|
}
|
|
|
|
void
|
|
kevent_update(int kqfd, struct kevent *kev)
|
|
{
|
|
if (kevent(kqfd, kev, 1, NULL, 0, NULL) < 0) {
|
|
printf("Unable to add the following kevent:\n%s\n",
|
|
kevent_to_str(kev));
|
|
die("kevent");
|
|
}
|
|
}
|
|
|
|
void
|
|
kevent_update_expect_fail(int kqfd, struct kevent *kev)
|
|
{
|
|
if (kevent(kqfd, kev, 1, NULL, 0, NULL) >= 0) {
|
|
printf("Performing update should fail");
|
|
die("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)
|
|
{
|
|
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));
|
|
die("kevent");
|
|
}
|
|
}
|
|
|
|
/** Check kqueue echo's the event back to use correctly
|
|
*
|
|
* @param[in] kqfd we're adding events to.
|
|
* @param[out] kev the event we got passed back in
|
|
* the receipt.
|
|
* @param[in] ident Identifier for the event.
|
|
* @param[in] filter to add event to.
|
|
* @param[in] flags to set.
|
|
* @param[in] fflags to set.
|
|
* @param[in] data to set.
|
|
* @param[in] udata to set.
|
|
* @param[in] file this function was called from.
|
|
* @param[in] line this function was called from.
|
|
*/
|
|
void
|
|
_kevent_add_with_receipt(int kqfd, struct kevent *kev,
|
|
uintptr_t ident,
|
|
short filter,
|
|
u_short flags,
|
|
u_int fflags,
|
|
intptr_t data,
|
|
void *udata,
|
|
char const *file,
|
|
int line)
|
|
{
|
|
struct kevent receipt;
|
|
|
|
EV_SET(kev, ident, filter, flags | EV_RECEIPT, fflags, data, NULL);
|
|
if (kevent(kqfd, kev, 1, &receipt, 1, NULL) < 0) {
|
|
printf("Unable to add the following kevent:\n%s\n",
|
|
kevent_to_str(kev));
|
|
die("kevent");
|
|
}
|
|
|
|
|
|
#ifdef __FreeBSD__
|
|
/*
|
|
* FreeBSD doesn't return EV_RECEIPT in the receipt
|
|
* but does return it in all future kevents.
|
|
*/
|
|
kev->flags ^= EV_RECEIPT;
|
|
#endif
|
|
|
|
kev->flags |= EV_ERROR;
|
|
_kevent_cmp(kev, &receipt, file, line);
|
|
kev->flags ^= EV_ERROR; /* We don't expect this in future events */
|
|
|
|
#ifdef __FreeBSD__
|
|
/*
|
|
* Add this back as it'll be returned in future
|
|
* kevents.
|
|
*/
|
|
kev->flags |= EV_RECEIPT;
|
|
#endif
|
|
}
|
|
|
|
void
|
|
_kevent_cmp(struct kevent *expected, struct kevent *got, const char *file, int line)
|
|
{
|
|
/* XXX-
|
|
Workaround for inconsistent implementation of kevent(2)
|
|
*/
|
|
#if defined (__FreeBSD_kernel__) || defined (__FreeBSD__)
|
|
if (expected->flags & EV_ADD)
|
|
got->flags |= EV_ADD;
|
|
#endif
|
|
if (memcmp(expected, got, sizeof(*expected)) != 0) {
|
|
printf("[%s:%d]: kevent_cmp() failed:\n", file, line);
|
|
printf("expected %s\n", kevent_to_str(expected));
|
|
printf("but got %s\n", kevent_to_str(got));
|
|
abort();
|
|
}
|
|
}
|
|
|
|
void
|
|
_kevent_rv_cmp(int expected, int got, const char *file, int line)
|
|
{
|
|
if (expected != got) {
|
|
printf("[%s:%d]: kevent_rv_cmp() failed:\n", file, line);
|
|
printf("expected %u\n", expected);
|
|
printf("but got %u\n", got);
|
|
abort();
|
|
}
|
|
}
|