Linux Kernel Inotify Subsystem

Linux Kernel Inotify Subsystem

When dealing with Unix-style filesystems there must be a data structure that is capable of describing an object from that filesystem, such as a file or a directory. 

Linux is no exception, so here comes the Inode.

The data it represents includes access time, modification time, file owner, permissions and much more.

Directories are a special type of file. They consist in lists of names, or labels, assigned to inodes in the filesystem. In addition, they also need means of representing themselves and how to get to their parent level. Here comes the "./" and the "../", respectively.

What if you need a way of keeping track of all these possible metadata operations on files and directories? 

Well, if that is the case, then you are thinking about the Kernel's Inotify subsystem. It acts as an extension to the filesystem, which provides information on changes to that filesystem. It is worth mentioning that the current inotify is a replacement for the dnotify, as of kernel version 2.6.13 (29-08-2005).

An example of the importance of the inotify subsystem relies on the simple fact that your file explorer (Dolphin, Nautilus, Thunar, Xfe, etc.) will simply update and redraw its view based on file/directory changes. Like the "update" on the total directory space, file modification time, etc.

To see inotify in action, here is a simple program that prints to the screen any modification you make to the provided file as the command line argument.

/* 
 * Test kernel's inotify subsystem 
 */


#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/inotify.h>


#ifndef MAXIMUM_LENGHT
#define MAXIMUM_LENGHT 255
#endif


#ifndef BUFFER
#define BUFFER (2048)
#endif


static void printEvent(struct inotify_event *);


int main(int argc, char *argv[]) {
    int inotify_fd;
    int watcher;
    int watched;
    char buffer[BUFFER];
    ssize_t num_read;
    char *ptr;
    struct inotify_event *event;


    if (argc < 2 || strcmp(argv[1], "--help") == 0 )
        fprintf(stdout, "Usage: %s file|dir\n", argv[0]);


    // ask for our inotify instance
    inotify_fd = inotify_init();    


    // tell the kernel what file(s) are of interest (from cmd line)
    for (watched = 1; watched < argc; watched++) {
        /* Watch all events */
        watcher = inotify_add_watch(inotify_fd, argv[watched], IN_ALL_EVENTS);
        if (watcher == -1) {
            fprintf(stderr,"error:inotify_add_watch");
            exit(EXIT_FAILURE);
        }


        fprintf(stdout,"%s watched by %d\n", argv[watched], watcher);
    }


    // read events forever
    for (;;) {
        num_read = read(inotify_fd, buffer, BUFFER);
        if (num_read == 0) {
            fprintf(stderr,"read() from inotify fd returned 0!");
            exit(EXIT_FAILURE);
        }


        if (num_read == -1) {
            fprintf(stderr, "read error!");
            exit(EXIT_FAILURE);
        }


        fprintf(stdout,"Read %ld bytes from inotify descriptor\n", (long) num_read);


        /* Process all of the events in buffer returned by read() */
        for (ptr = buffer; ptr < buffer + num_read; ) {
            event = (struct inotify_event *) ptr;
            printEvent(event);


            /* go to next event */
            ptr += sizeof(struct inotify_event) + event->len;
        }
    }


    exit(EXIT_SUCCESS);
}


static void printEvent(struct inotify_event *e) {
    // Do we have any "saved" information?
    if (e->cookie > 0)
        fprintf(stdout, "cookie = 4%d; ", e->cookie);


    fprintf(stdout, "Inotify Bit Mask = ");
    if (e->mask & IN_OPEN)          fprintf(stdout, "IN_OPEN");
    if (e->mask & IN_ISDIR)         fprintf(stdout, "IN_ISDIR");
    if (e->mask & IN_ACCESS)        fprintf(stdout, "IN_ACCESS");
    if (e->mask & IN_ATTRIB)        fprintf(stdout, "IN_ATTRIB");
    if (e->mask & IN_DELETE)        fprintf(stdout, "IN_DELETE");
    if (e->mask & IN_CREATE)        fprintf(stdout, "IN_CREATE");
    if (e->mask & IN_MODIFY)        fprintf(stdout, "IN_MODIFY");
    if (e->mask & IN_IGNORED)       fprintf(stdout, "IN_IGNORED");
    if (e->mask & IN_UNMOUNT)       fprintf(stdout, "IN_UNMOUNT");
    if (e->mask & IN_MOVED_TO)      fprintf(stdout, "IN_MOVED_TO");
    if (e->mask & IN_MOVE_SELF)     fprintf(stdout, "IN_MOVE_SELF");
    if (e->mask & IN_Q_OVERFLOW)    fprintf(stdout, "IN_Q_OVERFLOW");
    if (e->mask & IN_MOVED_FROM)    fprintf(stdout, "IN_MOVED_FROM");
    if (e->mask & IN_DELETE_SELF)   fprintf(stdout, "IN_DELETE_SELF");
    if (e->mask & IN_CLOSE_WRITE)   fprintf(stdout, "IN_CLOSE_WRITE");
    if (e->mask & IN_CLOSE_NOWRITE) fprintf(stdout, "IN_CLOSE_NOWRITE");
    fprintf(stdout, "\n");


    /* Is there a name? */
    if ( e->len > 0)
        fprintf(stdout, "    name = %s\n", e->name);
}

To run this program, you just need to compile it

gcc -o inotify_test inotify_example.c

And fire it up against any file you want...

./inotify_test inotify_example.c 

If you simply save that file being watched, you can see the program printing information regarding changes.

inotify_example.c watched by 1
Read 16 bytes from inotify descriptor
Inotify Bit Mask = IN_OPEN
Read 16 bytes from inotify descriptor
Inotify Bit Mask = IN_ACCESS
Read 16 bytes from inotify descriptor
Inotify Bit Mask = IN_CLOSE_NOWRITE
Read 32 bytes from inotify descriptor
Inotify Bit Mask = IN_MODIFY
Inotify Bit Mask = IN_OPEN
Read 16 bytes from inotify descriptor
Inotify Bit Mask = IN_MODIFY
Read 16 bytes from inotify descriptor
Inotify Bit Mask = IN_CLOSE_WRITE


要查看或添加评论,请登录

Gabriel M.的更多文章

  • Docker 101

    Docker 101

    A (tiny) introduction to Docker. If you want to get your hands dirty with some Docker content, this quick introduction…

  • Building custom kernels for Linux + updating the system firmware.

    Building custom kernels for Linux + updating the system firmware.

    In this post I'll show you how you can replace your current Linux kernel with a new one built entirely from the…

  • Debian complained about missing firmware during installation? Add missing 'non-free' firmware to installation image! :)

    Debian complained about missing firmware during installation? Add missing 'non-free' firmware to installation image! :)

    If you have installed Debian, you might have faced the screen below: And probably got very frustrated, because your…

  • Using Traps in Bash Scripts

    Using Traps in Bash Scripts

    Imagine that you have a script to perform some critical operation, one that would render the system completely unusable…

    2 条评论
  • Safer Bash Scripting Tips

    Safer Bash Scripting Tips

    I hope you are all well and safe. As "stay safe" is the most used expression during this COVID-19 period, I thought it…

    1 条评论
  • Pointers and 2D-arrays in C

    Pointers and 2D-arrays in C

    When taking the Engineering or CS undergrad path, like 1+1 leads to 2 (let's keep it simple here, shall we?), students…

    2 条评论
  • Linux File I/O

    Linux File I/O

    When using system calls for dealing with file I/O, open(), read(), write() and close() are the four functions used to…

  • Shifting bits in C

    Shifting bits in C

    Have you ever asked yourself how much memory space do you waste when writing your code? Sometimes you just need a…

  • Improving your Linux box security

    Improving your Linux box security

    Did you know that more than never, during these quarantine days, there is a lot more malicious activities undergoing…

  • Build UnrealEngine on Linux, making it use less disk space! :)

    Build UnrealEngine on Linux, making it use less disk space! :)

    If you are playing with the amazing Unreal Engine on your Linux box, you might have noticed that the final size after…

社区洞察

其他会员也浏览了