#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

#include <linux/inotify.h>

# define __NR_inotify_init      291
# define __NR_inotify_add_watch 292
# define __NR_inotify_rm_watch  293

#define TMP_TEMPLATE "/tmp/inotify.XXXXXXXX"

static unsigned long long tvdiff(struct timeval a, struct timeval b)
{
        unsigned long long all = a.tv_sec*1000000ll+a.tv_usec;
        unsigned long long bll = b.tv_sec*1000000ll+b.tv_usec;

        return bll-all;
}

int main(int argc, char **argv)
{
        int idev, loops;
        char *fname;
        char **tmpfiles;
        int *tmpwds;
        int fd, i;

        struct timeval tv_pre[3], tv_post[3];
        struct rusage usage_pre[3], usage_post[3];

        if (argc < 2) {
                fprintf(stderr, "usage: %s <iterations>\n",
                        argv[0]);
                return -1;
        }
        loops = atoi(argv[1]);

        if ((idev = syscall(__NR_inotify_init)) < 0) {
                perror("inotify_init");
                return -1;
        }
        
        tmpfiles = malloc(sizeof(char *) * loops);
        if (!tmpfiles) {
                perror("malloc");
                return -1;
        }
        memset(tmpfiles, 0, sizeof(char *) * loops);

        tmpwds = malloc(sizeof(int) * loops);
        if (!tmpwds) {
                free(tmpfiles);
                perror("malloc");
                return -1;
        }
        memset(tmpwds, 0, sizeof(int) * loops);
                
        for (i = 0; i < loops; i++) {

                fname = (char *)malloc(strlen(TMP_TEMPLATE) + 1);
                if (!fname) {
                        perror("malloc");
                        goto cleanup;
                }

                strcpy(fname, TMP_TEMPLATE);
                fd = mkstemp(fname);
                if (fd < 0) {
                        perror("mkstemp");
                        free(fname);
                        goto cleanup;
                }
                close(fd);

                tmpfiles[i] = fname;
        }

        gettimeofday(&tv_pre[0], 0);
        getrusage(RUSAGE_SELF, &usage_pre[0]);
        for (i = 0; i < loops; i++) {
                tmpwds[i] = syscall(__NR_inotify_add_watch, idev, tmpfiles[i],
                                    IN_ACCESS);
                if (tmpwds[i] < 0)
                        perror("inotify_add_watch");
        }
        gettimeofday(&tv_post[0] ,0);
        getrusage(RUSAGE_SELF, &usage_post[0]);

        gettimeofday(&tv_pre[1], 0);
        getrusage(RUSAGE_SELF, &usage_pre[1]);
        for (i = 0; i < loops; i++) {
                if (syscall(__NR_inotify_add_watch, idev, tmpfiles[i],
                            IN_MODIFY|IN_MASK_ADD) < 0)
                        perror("inotify_add_watch");
        }
        gettimeofday(&tv_post[1], 0);
        getrusage(RUSAGE_SELF, &usage_post[1]);

        gettimeofday(&tv_pre[2], 0);
        getrusage(RUSAGE_SELF, &usage_pre[2]);
        for (i = 0; i < loops; i++) {
                if (syscall(__NR_inotify_rm_watch, idev, tmpwds[i]) < 0)
                        perror("inotify_rm_watch");
        }
        gettimeofday(&tv_post[2], 0);
        getrusage(RUSAGE_SELF, &usage_post[2]);

        close(idev);

        printf("Iterations: %d\n", loops);

        printf("\nadd watch average:\n");
        printf("real  %8llu usec\n", tvdiff(tv_pre[0], tv_post[0])/loops);
        printf("user  %8llu usec\n", tvdiff(usage_pre[0].ru_utime, usage_post[0].ru_utime)/loops);
        printf("sys   %8llu usec\n", tvdiff(usage_pre[0].ru_stime, usage_post[0].ru_stime)/loops);

        printf("\nupdate watch average:\n");
        printf("real  %8llu usec\n", tvdiff(tv_pre[1], tv_post[1])/loops);
        printf("user  %8llu usec\n", tvdiff(usage_pre[1].ru_utime, usage_post[1].ru_utime)/loops);
        printf("sys   %8llu usec\n", tvdiff(usage_pre[1].ru_stime, usage_post[1].ru_stime)/loops);

        printf("\nremove watch average:\n");
        printf("real  %8llu usec\n", tvdiff(tv_pre[2], tv_post[2])/loops);
        printf("user  %8llu usec\n", tvdiff(usage_pre[2].ru_utime, usage_post[2].ru_utime)/loops);
        printf("sys   %8llu usec\n", tvdiff(usage_pre[2].ru_stime, usage_post[2].ru_stime)/loops);

cleanup:
        for (i = 0; i < loops; i++) {
                if (!tmpfiles[i])
                        break;
                unlink(tmpfiles[i]);
                free(tmpfiles[i]);
                tmpfiles[i] = NULL;
        }
        free(tmpfiles);
        free(tmpwds);

    return 0;
}
