# kqueue/kevent determine filename from file description



## pelmen (Aug 6, 2011)

Hi

I want to watch directory for modification by kqueue mechanism. Simple code:

```
#include <dirent.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>    /* for strerror () */
#include <unistd.h>
#include <sys/event.h>

#include <sys/types.h>
#include <dirent.h>

#define MAX_ENTRIES 256

/* Function prototypes */
void diep(const char *s);

int main(int argc, char *argv[])
{
    struct kevent evlist[MAX_ENTRIES];    /* events we want to monitor */
    struct kevent chlist[MAX_ENTRIES];    /* events that were triggered */
    struct dirent *pdent;
    DIR *pdir;
    char fullpath[256];
    int fdlist[MAX_ENTRIES], cnt, i, error, kq, nev;

    /* Check argument count */
    if (argc != 2) {
        fprintf(stderr, "Usage: %s directory\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    /* Create a new kernel event queue */
    if ((kq = kqueue()) == -1)
        diep("kqueue()");

    /*
     * Open directory named by argv[1], associate a directory stream
     * with it and return a pointer to it.
     */
    if ((pdir = opendir(argv[1])) == NULL)
        diep("opendir()");

    /* Skip . and .. entries */
    cnt = 0;
    while((pdent = readdir(pdir)) != NULL && cnt++ < 2)
        ;    /* VOID */

    /*
     * Get all directory entries and for each one of them,
     * initialise a kevent structure.
    */
    cnt = 0;
    while((pdent = readdir(pdir)) != NULL) {
	/*
         * Check whether we have exceeded the max number of
         * entries that we can monitor.
         */
        if (cnt > MAX_ENTRIES - 1) {
            fprintf(stderr, "Max number of entries exceeded\n");
            goto CLEANUP_AND_EXIT;
        }

        /*
         * Check path length
         * don't forget +1 for the '\0'
         */
        if (strlen(argv[1] + strlen(pdent->d_name) + 2) > 256) {
            fprintf(stderr,"Max path length exceeded\n");
            goto CLEANUP_AND_EXIT;
        }
        strcpy(fullpath, argv[1]);
        strcat(fullpath, "/");
        strcat(fullpath, pdent->d_name);

	/* Open directory entry */
        if ((fdlist[cnt] = open(fullpath, O_RDONLY)) == -1) {
            perror("open()");
	    continue;
        }

        /* Initialise kevent structure */
	EV_SET(&chlist[cnt], fdlist[cnt], EVFILT_VNODE,
               EV_ADD | EV_ENABLE | EV_ONESHOT,
               NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | NOTE_LINK | NOTE_RENAME | NOTE_REVOKE,
               0, 0);

        cnt++;
    }

    /* Loop forever */
    for (;;) {
        nev = kevent(kq, chlist, cnt, evlist, cnt, NULL);
        if (nev == -1)
            perror("kevent()");

        else if (nev > 0) {
            for (i = 0; i < nev; i++) {
                if (evlist[i].flags & EV_ERROR) {
                    fprintf(stderr, "EV_ERROR: %s\n", strerror(evlist[i].data));
                    goto CLEANUP_AND_EXIT;
                }

                if (evlist[i].fflags & NOTE_DELETE)
                    printf("fd: %d Deleted\n", evlist[i].ident);

                else if (evlist[i].fflags & NOTE_EXTEND
                         || evlist[i].fflags & NOTE_WRITE)
                    printf("fd: %d Modified\n", evlist[i].ident);

                else if (evlist[i].fflags & NOTE_ATTRIB)
                    printf("fd: %d Attributes modified\n", evlist[i].ident);

                else if (evlist[i].fflags & NOTE_LINK)
                    printf("fd: %d link\n", evlist[i].ident);

                else if (evlist[i].fflags & NOTE_RENAME)
                    printf("fd: %d rename\n", evlist[i].ident);

                else if (evlist[i].fflags & NOTE_REVOKE)
                    printf("fd: %d revoke\n", evlist[i].ident);



            }
        }
    }

    /* Clean up file descriptors, directory stream,  kqueue */
 CLEANUP_AND_EXIT:;
    error = EXIT_SUCCESS;
    for (i = 0; i < cnt; i++)
        if (close(fdlist[i]) == -1) {
            error = EXIT_FAILURE;
            perror("close");
        }

    if ((closedir(pdir) == -1) || (close(kq) == -1)) {
        error = EXIT_FAILURE;
        perror("close");
    }

    return error;
}

void diep(const char *s)
{
    perror(s);
    exit(EXIT_FAILURE);
}
```

After XXX hours of googling, I still can not figure out how I can get the name of the newly created file. 
For get the filename that already was on the kqueue initialization - I can fill the structure from *fullpath* (pdent->d_name). 
I see one way - have one copy of files and after creation compare lists and see what has been added. Also i need to  re-initialization of the monitor to restart for monitoring newly created file. but I think it's stupid. In the Linux event/inotify mechanism i can get the file name from @->userdata data. 
I am right that in the case of kqueue I have to constantly re-read and compare the lists to get the name of the created file? Thanks.


----------



## expl (Aug 7, 2011)

```
EV_SET(_kev, ident, filter, flags, fflags, data, udata);

struct kevent {
	     uintptr_t ident;	     /* identifier for this event */
	     short     filter;	     /* filter for event */
	     u_short   flags;	     /* action flags for kqueue */
	     u_int     fflags;	     /* filter flag value */
	     intptr_t  data;	     /* filter data value */
	     void      *udata;	     /* opaque user data identifier */
};
```

Do you see udata? Thats where you should put/get your file name.


----------



## pelmen (Aug 7, 2011)

expl said:
			
		

> Do you see udata? Thats where you should put/get your file name.



Yea. But where can I get a filename that I can put in udata when i watching directory?



```
FILE *fp = fopen("/tmp", "r");
...
    EV_SET(&_kev, (*fp)._file, EVFILT_VNODE,
           EV_ADD | EV_ENABLE | EV_CLEAR | EV_ONESHOT,
           NOTE_DELETE | NOTE_WRITE | NOTE_EXTEND | NOTE_ATTRIB | NOTE_LINK | NOTE_RENAME | NOTE_REVOKE, 0, NULL);
...
```
When i do

*touch /tmp/newfile*

I see changes in /tmp but do not know what is changed


----------



## expl (Aug 7, 2011)

This is not possible with kevents, it does not even tell you if any file that belongs to the directory was added or removed. You just get NOTE_EXTEND event for the directory descriptor and you have to determine what happened on your own.


----------



## pelmen (Aug 9, 2011)

Ok, Thanks for detail


----------

