linux fcntl+F_NOTIFY not working as expected: when there's change, nothing triggered

751 views Asked by At

I'm on RHEL5 with kernel 2.6.32. Trying to see if fcntl+F_NOTIFY could work to monitor a directory or file change. I searched google and found this file:

#define _GNU_SOURCE

#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>

static volatile int event_fd;

static void handler(int signum, siginfo_t *si, void *data){
    event_fd = si->si_fd;
    printf("info size:%d, data:%d/n", sizeof(siginfo_t), sizeof(data));
}

int main(int argc, char **argv){
    struct sigaction action;
    int fd;
    action.sa_sigaction = handler;
    sigemptyset(&action.sa_mask);
    action.sa_flags = SA_SIGINFO;
    sigaction(SIGRTMIN+1, &action, NULL);

    fd = open("test", O_RDONLY);
    fcntl(fd, F_SETSIG, SIGRTMIN+1);
    fcntl(fd, F_NOTIFY, DN_MODIFY | DN_CREATE | DN_MULTISHOT);

    fd = open(".", O_RDONLY);
    fcntl(fd, F_SETSIG, SIGRTMIN+1);
    fcntl(fd, F_NOTIFY, DN_MODIFY | DN_CREATE | DN_MULTISHOT);
    while(1){
        pause();
        printf("got event on fd=%d/n", event_fd);
    }
}

I compiled it to be ./a.out.

In another terminal I created an empty file called "test.txt". Then I started ./a.out. Now matter how I change "test.txt" or touch/remove files under "." there's no output by ./a.out

Am I understanding anything wrong? I expect that when I change "test.txt" or change current directories any operation, it should trigger my "handler" function and print out something.

Any explanations?

Thanks for Andrew's explanations, yes it's a mis-typing plus and asyn problem, the correction is as follows:

#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
static volatile int event_fd;
static void handler(int signum, siginfo_t *si, void *data){
    event_fd = si->si_fd;
    char msg[100];
    sprintf(msg, "info size:%d, data:%d\n", sizeof(siginfo_t), sizeof(data));
    write(STDOUT_FILENO, msg, strlen(msg));
}
int main(int argc, char **argv){
    struct sigaction action;
    int fd;
    action.sa_sigaction = handler;
    sigemptyset(&action.sa_mask);
    action.sa_flags = SA_SIGINFO;
    sigaction(SIGRTMIN+1, &action, NULL);

    fd = open("./test.txt", O_RDONLY);
    fcntl(fd, F_SETSIG, SIGRTMIN+1);
    fcntl(fd, F_NOTIFY, DN_MODIFY | DN_CREATE | DN_MULTISHOT);

    fd = open(".", O_RDONLY);
    fcntl(fd, F_SETSIG, SIGRTMIN+1);
    fcntl(fd, F_NOTIFY, DN_MODIFY | DN_CREATE | DN_MULTISHOT);
    while(1){
        pause();
        char msg[100];
        sprintf(msg, "got event on fd=%d\n", event_fd);
        write(STDOUT_FILENO, msg, strlen(msg));
    }
}

And so it works. Thanks very much.

1

There are 1 answers

0
Andrew Henle On BEST ANSWER

First, calling printf() inside a signal handler is unsafe. Only async-signal-safe functions can be safely called from within a signal handler. The POSIX-specified list of async-signal-safe functions can be found at http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04

Note that printf() is not one of the async-signal-safe functions.

Second, your printf() function call is likely line-buffered, and you don't terminate your output with a newline:

printf("info size:%d, data:%d/n", sizeof(siginfo_t), sizeof(data));

Note that you end the string with "/n", which is a forward slash '/' followed by the character 'n', not a newline, which would be the backslash '\' followed by 'n', or "\n". Assuming your stdout stream is the Linux default line-buffered, no output will be emitted until the output buffer is full, probably 4 or 8 kB. If it works at all and doesn't deadlock, because printf() is an async-signal-unsafe function.