Patrick Kelley 8fd444092b initial
2025-05-07 15:35:15 -04:00

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();
}
}