The following program has been used:
```
#include <sys/event.h>
#include <sys/types.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
void test_event(int fd, int kq, const char *what, u_short flag)
{
char buf = 'a';
printf(" %s -> ", what);
struct kevent event[2], tevent;
EV_SET(&event[0], fd, flag, EV_ADD | EV_CLEAR, 0, 0, NULL);
EV_SET(&event[1], fd, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, 1, NULL);
int rc = kevent(kq, event, 2, NULL, 0, NULL);
if (rc == -1)
{
err(EXIT_FAILURE, "kevent (set)");
}
rc = kevent(kq, NULL, 0, &tevent, 1, NULL);
if (rc == -1)
{
err(EXIT_FAILURE, "kevent (wait)");
}
else if (rc > 0)
{
if (tevent.flags & EV_ERROR)
{
err(EXIT_FAILURE, "kevent (flags)");
}
else
{
switch (tevent.filter)
{
case EVFILT_READ:
printf("read triggered -> ");
rc = read(fd, &buf, 1);
if (rc == -1)
{
printf("error: %s\n", strerror(errno));
}
else if (rc == 0)
{
printf("ok, EOF\n");
}
else
{
printf("ok, %0x\n", buf);
}
break;
case EVFILT_WRITE:
printf("write triggered -> ");
rc = write(fd, &buf, 1);
if (rc == -1)
{
printf("error: %s\n", strerror(errno));
}
else
{
printf("ok, written\n");
}
break;
case EVFILT_TIMER:
printf("timeout\n");
break;
default:
printf("unknown event triggered\n");
break;
}
}
}
}
void test(const char* path)
{
printf("%s\n", path);
int fd = open(path, O_RDWR);
if (fd == -1)
{
err(EXIT_FAILURE, "open failed: %s\n", path);
}
int kq = kqueue();
if (kq == -1)
{
err(EXIT_FAILURE, "kqueue failed\n");
}
test_event(fd, kq, "read", EVFILT_READ);
test_event(fd, kq, "write", EVFILT_WRITE);
close(kq);
close(fd);
}
int main()
{
test("/dev/null-o");
test("/dev/full-o");
test("/dev/zero-o");
return 0;
}
```
The following output is expected:
```
/dev/null
read -> read triggered -> ok, EOF
write -> write triggered -> ok, written
/dev/full
read -> read triggered -> ok, 0
write -> timeout
/dev/zero
read -> read triggered -> ok, 0
write -> write triggered -> ok, written
```