#include <signal.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#include <linux/unistd.h>
#include <linux/types.h>

#include <linux/ukevent.h>

#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \
type name (type1 arg1, type2 arg2, type3 arg3) \
{\
	return syscall(__NR_##name, arg1, arg2, arg3);\
}

#define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \
type name (type1 arg1, type2 arg2, type3 arg3, type4 arg4) \
{\
	return syscall(__NR_##name, arg1, arg2, arg3, arg4);\
}

#define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \
	  type5,arg5) \
type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5) \
{\
	return syscall(__NR_##name, arg1, arg2, arg3, arg4, arg5);\
}

#define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \
	  type5,arg5,type6,arg6) \
type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5, type6 arg6) \
{\
	return syscall(__NR_##name, arg1, arg2, arg3, arg4, arg5, arg6);\
}

_syscall4(int, kevent_ctl, int, arg1, unsigned int, argv2, unsigned int, argv3, void *, argv4);
_syscall6(int, kevent_get_events, int, arg1, unsigned int, argv2, unsigned int, argv3, __u64, argv4, void *, argv5, unsigned, arg6);
_syscall4(int, kevent_wait, int, arg1, unsigned int, arg2, unsigned int, argv3, __u64, argv4);
_syscall3(int, kevent_ring_init, int, arg1, struct kevent_ring *, arg2, unsigned int, argv3);

static void sig_handler(int signo)
{
	printf("%s, signo: %d.\n", __func__, signo);
}

static int kevent_add(int ctl_fd, int signo, int nodel)
{
	struct ukevent uk;
	int err;

	uk.event = KEVENT_SIGNAL_DELIVERY;
	uk.type = KEVENT_SIGNAL;
	uk.id.raw[0] = signo;
	if (nodel)
		uk.id.raw_u64 |= KEVENT_SIGNAL_NOMASK;
	uk.req_flags = KEVENT_REQ_ET;

	err = kevent_ctl(ctl_fd, KEVENT_CTL_ADD, 1, &uk);
	if (err < 0) {
		fprintf(stderr, "Failed to add kevent: signo: %d, nodel: %d: %s [%d].\n", 
				signo, nodel, strerror(errno), errno);
		return err;
	} else if (err == 1) {
		fprintf(stderr, "Ready immediately, need to requeue: signo: %d, nodel: %d.\n", 
				signo, nodel);
		return err;
	}

	return 0;
}

int main()
{
	int fd, err;

	signal(SIGUSR1, sig_handler);
	signal(SIGUSR2, sig_handler);

	fd = open("/dev/kevent", O_RDWR);
	if (fd == -1) {
		fprintf(stderr, "Failed to open /dev/kevent: %s [%d].\n", strerror(errno), errno);
		return -1;
	}

	err = kevent_add(fd, SIGUSR1, 0);
	if (err)
		return err;

	err = kevent_add(fd, SIGUSR2, 1);
	if (err)
		return err;

	while (1) {
		struct ukevent uk;

		err = kevent_get_events(fd, 1, 1, 100000000, &uk, 0);

		if (err < 0) {
			fprintf(stderr, "Failed to get events: %s [%d].\n", strerror(errno), errno);
			break;
		} else if (err == 0)
			continue;

		printf("Dequeued signal: %u, nodel: %d.\n", uk.id.raw[0], ((uk.id.raw_u64 & KEVENT_SIGNAL_NOMASK) == KEVENT_SIGNAL_NOMASK));
	}

	return 0;
}
