Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Use poll() to check for file modification

Tags:

c

linux

There is a file and I would like to check it with poll() that the contents have changed.

On Raspi there is a file called gpio value, if the value changed the poll() was triggered with POLLPRI, I would like to do the same with an ordinary file.

Here is the code I am using to check the GPIO file:

int gpio_fd = gpio_fd_open();
int timeout = POLL_TIMEOUT;
struct pollfd fdset;
int nfds = 1;
char *buf[MAX_BUF];
int len;
int rc;
fdset.fd = gpio_fd;
fdset.events =  POLLPRI | POLLERR | POLLHUP | POLLNVAL; // POLLIN | | POLLOUT 
unsigned int c1, c2, c3;
do{
    rc = poll(&fdset, 1, timeout);

And the gpio_fd_open function:

int gpio_fd_open() {
    printf("opening File: " SYSFS_GPIO_DIR "\n");

    int fd, len;
    char buf[MAX_BUF];

    len = snprintf(buf, sizeof (buf), SYSFS_GPIO_DIR);
    fd = open(buf, O_RDONLY | O_NONBLOCK);
    if (fd < 0) {
        perror("gpio/fd_open");
    }
    return fd;
}
like image 544
Text Texer Avatar asked Mar 04 '15 10:03

Text Texer


1 Answers

In Linux, unlike with your special GPIO file, you can not poll the fd to an open ordinary file like that.

To watch a file for changes you can use the inotify family of functions. Conveniently for you they use a file descriptor which can be passed to poll() along with your GPIO file descriptor, so you can watch both at the same time.

So, some minor additions/changes to your code:

int fw_fd = file_watch_fd("/some/file/to/watch");
struct pollfd fdset[2];
int nfds = 2;
int rc;
fdset[0].fd = gpio_fd;
fdset[0].events =  POLLPRI | POLLERR | POLLHUP | POLLNVAL;
fdset[1].fd = fw_fd;
fdset[1].events = POLLIN;
do {
    rc = poll(fdset, nfds, timeout);

You are polling on the inotify fd, which you read to return one more watched events which occurred. Since this code is only watching one file for one event, we're pretty sure what the event is, but we still need to read it out of the fd, then we can actually read the file.

    if (fdset[1].revents & POLLIN) {
        if (ready(fw_fd)) {
            /* file has changed, read it */
        }
    }

Here is the file_watch_fd() function:

int file_watch_fd ( const char * filename ) {
        static int      inot    = ERR;
        static int      iflags  = IN_CLOEXEC | IN_NONBLOCK;
        static uint32_t mask    = IN_MODIFY;
        int             watch;

        if (inot == ERR) {
                inot = inotify_init1(iflags);
                if (inot == ERR) return ERR;
        }
        watch = inotify_add_watch(inot, filename, mask);
        if (watch == ERR) return ERR;
        return inot;
}

And here is the ready() function:

int ready ( int inot ) {
        uint64_t                buffer[8192];
        ssize_t                 nr;
        char *                  p;
        size_t                  n;
        struct inotify_event *  evP;
        int                     ready   = 0;

        while ((nr = read(inot, (char *)buffer, sizeof(buffer))) > 0) {
                for (p = buffer; p < buffer + nr; p += n) {
                        evP = (struct inotify_event *)p;
                        if (evP->mask & IN_MODIFY) ready = 1;
                        n = sizeof(struct inotify_event) + evP->len;
                }
        }
        return ready;
}

(People experience in the use of inotify will notice that I ran roughshod over the functionality of it to keep this example as simple as I can)

like image 78
John Hascall Avatar answered Sep 28 '22 08:09

John Hascall