#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 _syscall2(type,name,type1,arg1,type2,arg2) \
type name (type1 arg1, type2 arg2) \
{\
	return syscall(__NR_##name, arg1, arg2);\
}

#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);\
}

_syscall3(int, kevent_init, struct kevent_ring *, arg1, unsigned int, arg2, unsigned int, arg3);
_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, struct timespec, argv4, void *, argv5, unsigned, arg6);
_syscall5(int, kevent_wait, int, arg1, unsigned int, arg2, unsigned int, argv3, struct timespec, argv4, unsigned int, argv5);
_syscall3(int, kevent_commit, int, arg1, unsigned int, arg2, unsigned int, arg3);

#define SIGEV_KEVENT 8

int main()
{
	int fd, err, i;

	fd = kevent_init(NULL, 0, 0);
	if (fd == -1) {
		fprintf(stderr, "Failed to open /dev/kevent: %s [%d].\n", strerror(errno), errno);
		return -1;
	}
	
	for (i=0; i<1; ++i) {
		struct sigevent ev;
		timer_t timer;
		struct itimerspec iter;

		ev.sigev_value.sival_int = 10;
		ev.sigev_signo = SIGALRM;
		ev.sigev_notify = SIGEV_KEVENT;
#ifdef GLIBC_IS_READY_WITH_ASM_SIGINFO_HEADER
		ev._sigev_un.kevent_fd = fd;
#else
		ev._sigev_un._tid = fd;
#endif

		err = timer_create(CLOCK_REALTIME, &ev, &timer);
		if (err) {
			perror("Failed to create posix timer");
			return -1;
		}

		memset(&iter, 0, sizeof(iter));
		iter.it_interval.tv_sec = time(NULL) + 2;
		iter.it_value.tv_sec = time(NULL) + 2;

		err = timer_settime(timer, TIMER_ABSTIME, &iter, NULL);
		if (err) {
			perror("Failed to setup posix timer");
			return -1;
		}

		printf("timer has been setup.\n");
	}

	while (1) {
		struct ukevent uk;
		struct timespec tm;

		tm.tv_sec = 1;
		tm.tv_nsec = 0;

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

		printf("%08x.%08x - %08x %p\n", uk.id.raw[0], uk.id.raw[1], uk.ret_flags, uk.ptr);
	}

	return 0;
}

