Search code examples
clinuxinotify

How to stop the repetition of inotify result?


In this code, I am trying to monitor two paths at the same time. I used while(1) for this purpose. But the problem that I am facing is that whenever I run the code, it gives me the same result two times like this.

Giving result

Pathname1 "file" is modified 
Pathname1 "file" is modified

Expected result

Pathname1 "file" is modified 

I debugged the code. After breaking the main function and stepping over it, the next command stops at this line length = read(fd, buffer, EVENT_BUF_LEN ). Whenever I break a line after this length variable command, the program starts and after modifying the file, the program stops at this line struct inotify_event *event = ( struct inotify_event *)&buffer[i]; Although the program should not break.

I also used IN_CLOSE_WRITE instead of IN_MODIFY but no change in the result.

typedef struct{
    int length, fd, wd1, wd2;
    char buffer[4096] __attribute__ ((aligned(__alignof__(struct inotify_event))));
} notification;
notification inotify;

int getNotified(char *pathname1, char *pathname2){
    inotify.fd = inotify_init();
    inotify.wd1 = inotify_add_watch(inotify.fd, pathname1, IN_MODIFY);
    inotify.wd2 = inotify_add_watch(inotify.fd, pathname2, IN_MODIFY);

    while(1){
        inotify.length = read(inotify.fd, inotify.buffer, EVENT_BUF_LEN); 
        int i = 0;
        while(i < inotify.length){     
            struct inotify_event *event = (struct inotify_event *)&inotify.buffer[i];
            if(event->len){
                if(event->mask & IN_MODIFY){
                    if(event->wd == inotify.wd1){
                        printf("Pathname1 '%s' is modified\n", event->name);
                        break;
                    }
                    if(event->wd == inotify.wd2){
                        printf("Pathname2 '%s' is modified\n", event->name);
                        break;
                    }
                }
            }
            i += EVENT_SIZE + event->len;
        }
    }
    inotify_rm_watch(inotify.fd, inotify.wd1);
    inotify_rm_watch(inotify.fd, inotify.wd2);

    close(inotify.fd);
    exit(0);
}

Solution

  • Some remarks:

    • You should not "break" as you go out of the inside "while" loop and call read() again
    • The IN_MODIFY event does not fill the event->name field (i.e. event->len = 0)
    • The number of IN_MODIFY events depends on how you modify the files ("echo xxx >> file" = write only = 1 IN_MODIFY; "echo xxx > file" = truncate + write = 2 IN_MODIFY)

    Here is a proposition for your program (with additional prints):

    #include <stdio.h>
    #include <sys/inotify.h>
    #include <unistd.h>
    #include <stdlib.h>
    
    #define EVENT_BUF_LEN 4096
    #define EVENT_SIZE sizeof(struct inotify_event)
    
    typedef struct{
      int length, fd, wd1, wd2;
      char buffer[EVENT_BUF_LEN] __attribute__ ((aligned(__alignof__(struct inotify_event))));
    } notification;
    
    notification inotify;
    
    int getNotified(char *pathname1, char *pathname2){
    
      inotify.fd = inotify_init();
      inotify.wd1 = inotify_add_watch(inotify.fd, pathname1, IN_MODIFY);
      printf("wd1 = %d\n", inotify.wd1);
      inotify.wd2 = inotify_add_watch(inotify.fd, pathname2, IN_MODIFY);
      printf("wd2 = %d\n", inotify.wd2);
    
      while(1){
        inotify.length = read(inotify.fd, inotify.buffer, EVENT_BUF_LEN); 
        int i = 0;
        printf("read() = %d\n", inotify.length);
        while(i < inotify.length){     
          struct inotify_event *event = (struct inotify_event *)&inotify.buffer[i];
          printf("event->len = %u\n", event->len);
          if(event->mask & IN_MODIFY){
            if(event->wd == inotify.wd1){
              printf("Pathname1 is modified\n");
            } else if (event->wd == inotify.wd2){
              printf("Pathname2 is modified\n");
            }
          }
          i += (EVENT_SIZE + event->len);
          printf("i=%d\n", i);
        }
      }
      inotify_rm_watch(inotify.fd, inotify.wd1);
      inotify_rm_watch(inotify.fd, inotify.wd2);
    
      close(inotify.fd);
      exit(0);
    }
    
    
    
    int main(void)
    {
      getNotified("/tmp/foo", "/tmp/bar");
    
      return 0;
    
    } // main
    

    Here is an example of execution:

    $ gcc notif.c -o notif
    $ > /tmp/foo
    $ > /tmp/bar
    $ ./notif 
    wd1 = 1
    wd2 = 2
    
    ====== Upon "> /tmp/foo": 1 event (truncate operation)
    read() = 16
    event->len = 0
    Pathname1 is modified
    i=16
    
    ====== Upon "echo qwerty > /tmp/foo": 2 events (write operation, one event for truncate operation and one for the write of "qwerty" at the beginning of the file)
    read() = 16
    event->len = 0
    Pathname1 is modified
    i=16
    read() = 16
    event->len = 0
    Pathname1 is modified
    i=16
    
    ====== Upon "echo qwerty >> /tmp/foo": 1 event (write of "qwerty" at the end of the file)
    read() = 16
    event->len = 0
    Pathname1 is modified
    i=16