#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, tv_post;
        struct rusage usage_pre, usage_post;

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

        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_pre, 0);
        getrusage(RUSAGE_SELF, &usage_pre);
        close(idev);
        gettimeofday(&tv_post, 0);
        getrusage(RUSAGE_SELF, &usage_post);

        printf("Iterations: %d\n", loops);
        printf("\nclose fd average:\n");
        printf("real  %8llu usec\n", tvdiff(tv_pre, tv_post)/loops);
        printf("user  %8llu usec\n", tvdiff(usage_pre.ru_utime, usage_post.ru_utime)/loops);
        printf("sys   %8llu usec\n", tvdiff(usage_pre.ru_stime, usage_post.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;
}
