F_NOTIFY in fcntl(2)?

Mel fbsd.questions at rachie.is-a-geek.net
Thu Apr 3 21:04:43 UTC 2008


On Thursday 03 April 2008 16:11:59 Mel wrote:
> On Thursday 03 April 2008 12:25:55 John Conover wrote:
> > Does freebsd support the F_NOTIFY, (i.e., File and directory change
> > notification,) in fcntl(2)?
> >
> > I get that it doesn't, but its an old 5X version, and I might have to
> > upgrade.
>
> Nope, this is a GNU extension to fcntl. File change notifications are done
> with EVFILT_VNODE using kqueue(2). Directory changes have no interface that
> I know of. You might wanna take a look at how devel/gamin handles this,
> specifically the kqueue implementation.

Figured I'd give an example of how it can be done (as in: works for me for 
file deletion/creation and renames). Doesn't work for utimes(2) operations on 
a file, as in "touch /tmp/this_file_exists" will not fire.
Code inlined below sig.

-- 
Mel

#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
#include <sysexits.h>
#include <err.h>
#include <stdio.h>
#include <sys/stat.h>
#include <time.h>
#include <fcntl.h>

int main(int argc, char **argv)
{
    int kq, fd;
    struct kevent changes, events;
    time_t *mtime;
    struct stat sb;

    if( -1 == (fd = open("/tmp/.", O_RDONLY)) )
        err(EX_OSERR, "Cannot open dir /tmp");

    if( NULL == (mtime = malloc(sizeof(time_t))) )
        err(EX_OSERR, "Failed to allocate %d bytes", sizeof(time_t));

    if( -1 == fstat(fd, &sb) )
        err(EX_OSERR, "Cannot stat fd %u", fd);
    
    *mtime = sb.st_mtime;

    if( -1 == (kq = kqueue()) )
        err(EX_OSERR, "Cannot get a kqueue");

    EV_SET(&changes, fd, EVFILT_TIMER, EV_ADD|EV_ENABLE, 0, 500,
        (void *)mtime);

    for( ;; )
    {

        /* we can only get one event, really. */
        if( -1 == kevent(kq, &changes, 1, &events, 1, NULL) )
            err(EX_OSERR, "kevent");

        if( events.flags & EV_ERROR )
            errc(EX_OSERR, events.data, "Event error");

        /* secretly, our timer is an fd, we probably should use udata for this
         * though.
         */
        if( -1 == fstat(events.ident, &sb) )
        {
            warn("Failed to stat fd %u", events.ident);
            break;
        }
        else
        {
            if( *mtime && *mtime != sb.st_mtime )
                printf("Mtime changed: %u => %u\n", *mtime, sb.st_mtime);
            else
                printf("Mtime unchanged: %u\n", *mtime);

            *mtime = sb.st_mtime;
        }
    }

    close(kq);
    return 0;
}


More information about the freebsd-questions mailing list