简体   繁体   中英

portable way to monitor a file for changes

I'm actually implementing a very simple version of tail(1). In FreeBSD, I use kqueue to monitor a file for changes and then printing appended lines to the output. But this is not a portable way, as kqueue is only available in BSD family. Is there a general, efficient and platform-independent way to monitor files for changes in UNIX? I prefer not to use external libraries.

This is the code I've written:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
#include <fcntl.h>
#include <unistd.h>

void die(const char*);

#define MAXLINE     1024

int main(int argc, char *argv[])
{
    int fdes;
    int kq;
    int nev;
    int flags;
    off_t curoff;

    char line[MAXLINE + 1];
    ssize_t nbytes;

    struct kevent change, event;

    if (2 != argc)
    {
        fprintf(stderr, "Usage: %s path\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    if (-1 == (fdes = open(argv[1], O_RDONLY)))
        die("open()");

    if (-1 == (curoff = lseek(fdes, 0, SEEK_END)))
        die("lseek()");

    int ch = 0;
    int i = 0;
    while (i < 10)
    {
        read(fdes, &ch, 1);

        if (ch == '\n')
            i++;

        if (10 > i)
            lseek(fdes, --curoff, SEEK_SET);
    }

    if (-1 == (flags = fcntl(fdes, F_GETFL)))
        die("fcntl()");

    flags |= O_NONBLOCK;

    if (-1 == fcntl(fdes, F_SETFL, flags))
        die("fcntl()1");

    while ((nbytes = read(fdes, line, MAXLINE)) > 0)
        if (write(STDOUT_FILENO, line, nbytes) != nbytes)
            die("write()");

    if (-1 == (kq = kqueue()))
        die("kqueue()");

    EV_SET(&change, fdes, EVFILT_VNODE, EV_ADD | EV_ENABLE | EV_ONESHOT,
     NOTE_EXTEND | NOTE_WRITE | NOTE_DELETE, 0, NULL);

    if (-1 == kevent(kq, &change, 1, NULL, 0, NULL))
        die("kevent()");

    for (;;)
    {
        if (-1 == (nev = kevent(kq, NULL, 0, &event, 1, NULL)))
            die("kevent()");

        if (nev > 0)
        {
            if (event.fflags & NOTE_WRITE || event.fflags & NOTE_EXTEND)    
            {
                while ((nbytes = read(fdes, line, MAXLINE)) > 0)
                    if (write(STDOUT_FILENO, line, nbytes) != nbytes)
                        die("write()");
            }
            else if (NOTE_DELETE & event.fflags)
            {
                printf("The file has been deleted\n");
                break;
            }
        }
    }

    return 0;
}

void die(const char *str)
{
    perror(str);
    exit(EXIT_FAILURE);
}

You can just keep doing the read() in a loop. If you read zero bytes, check for an error. If there is no error, then you've hit EOF. At EOF, stat() the filename, if it's gone, then the file was deleted. If stat returns, compare the st_dev and st_ino fields of the stat to the results from fstat (cache this when you open the file). If they're different, the path was deleted and re-created. Sleep as long as you care to after the delete check before trying another read.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM