Dropbox is a very useful cloud disk, you just need to set up a directory in Dropbox, Dropbox will automatically sync the files to the cloud whenever the files in that directory change.

So how does Dorpbox know that the files in the directory have changed? The answer is, through the inotfiy system function to achieve.

We are divided into two main articles to introduce the function of inotify: this article first introduces how inotify is used, and the next part mainly introduces the implementation principle of inotify.

The way the interface

There are only three interfaces in inotify_init, inotify_add_watch, and inotify_RM_watch. Let’s take a look at the roles and prototypes of these three interfaces.

1. inotify_init

The inotify_init function is used to create an inotify handle that you can think of as an inotify object. The prototype is as follows:

int inotify_init(void);
Copy the code

2. inotify_add_watch

Once the inotify_add_watch handle is created, you can add files or directories to listen on by calling the inotify_add_watch function. The prototype is as follows:

int inotify_add_watch(int fd, const char *path, uint32_t mask);
Copy the code

When the inotify_add_watch call succeeds, the descriptor of the file or directory being listened on is returned. Here are the meanings of each parameter:

  • Fd: is an inotify handle created through the inotify_init function.

  • Path: the path of the file or directory to listen on.

  • Mask: the event to listen for. The event type is as follows:

    • type describe
      IN_ACCESS File accessed
      IN_ATTRIB The file metadata is changed
      IN_CLOSE_WRITE Close a file that is opened for writing
      IN_CLOSE_NOWRITE Close files that are opened in read-only mode
      IN_CREATE A file/directory is created in the listening directory
      IN_DELETE Delete files/directories in the listening directory
      IN_DELETE_SELF The listening directory/file itself is deleted.
      IN_MODIFY The file is modified.
      IN_MOVE_SELF The monitored directory/file itself is moved
      IN_MOVED The file is moved
      IN_OPEN File opened
      IN_ALL_EVENTS All of the above output events

3. inotify_rm_watch

The inotify_rm_watch function is used to delete a monitored file or directory. Its prototype is as follows:

int inotify_rm_watch(int fd, uint32_t wd);
Copy the code

Here are the meanings of each parameter:

  • fd: callinotify_initFunction returnedinotifyHandle.
  • wdBy:inotify_add_watchThe function returns the descriptor of the file or directory being listened on.

Read change event

Now that we’ve introduced inotify’s interface, let’s look at a simple example to show how to use inotify. Before writing an example of inotify, let’s look at how to get changes to a monitored file or directory. Inotify does not provide a specific interface for retrieving changes to files or directories that are being listened on. Instead, inotify uses the generic read function. Here’s a prototype:

int read(int fd, void *events, size_t len);
Copy the code

Here’s what each parameter means:

  • fdBy:inotify_initTo create theinotifyHandle.
  • events: a buffer for changing events.
  • len: Size of the buffer.

Events is an array of inotify_event structures. The inotify_event structure is defined as follows:

struct inotify_event{
    int         wd;      // The descriptor of the monitored file or directory (inotify_add_watch)
    uint32_t    mask;    // Change events
    uint32_t    cookie;  // It can be ignored
    uint32_t    len;     // The length of name
    char        name[];  // Used to store changed file or directory names
};
Copy the code

Using the instance

Now we can write an example using inotify. This example shows how to use inotify to listen for a file or directory and print its changes.

The implementation code is as follows:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/inotify.h>  // Introduce the inotify header file

/* * Used to print events that occurred */
void display_event(const char *base, struct inotify_event *event)
{
    char *operate;
    int mask = event->mask;

    if (mask & IN_ACCESS)        operate = "ACCESS";
    if (mask & IN_ATTRIB)        operate = "ATTRIB";
    if (mask & IN_CLOSE_WRITE)   operate = "CLOSE_WRITE";
    if (mask & IN_CLOSE_NOWRITE) operate = "CLOSE_NOWRITE";
    if (mask & IN_CREATE)        operate = "CREATE";
    if (mask & IN_DELETE_SELF)   operate = "DELETE_SELF";
    if (mask & IN_MODIFY)        operate = "MODIFY";
    if (mask & IN_MOVE_SELF)     operate = "MOVE_SELF";
    if (mask & IN_MOVED_FROM)    operate = "MOVED_FROM";
    if (mask & IN_MOVED_TO)      operate = "MOVED_TO";
    if (mask & IN_OPEN)          operate = "OPEN";
    if (mask & IN_IGNORED)       operate = "IGNORED";
    if (mask & IN_DELETE)        operate = "DELETE";
    if (mask & IN_UNMOUNT)       operate = "UNMOUNT";

    printf("%s/%s: %s\n", base, event->name, operate);
}

#define EVENTS_BUF_SIZE 4096

int main(int argc, char const *argv[])
{
    int fd;
    int nbytes, offset;
    char events[EVENTS_BUF_SIZE];
    struct inotify_event *event;

    fd = inotify_init(); // Create an inotify handle
    if (fd < 0) {
        printf("Failed to initalize inotify\n");
        return - 1;
    }

    // Get the file or directory path to listen on from the command line argument
    // Add a file or directory to listen on, and listen for all events
    if (inotify_add_watch(fd, argv[1], IN_ALL_EVENTS) == - 1) {
        printf("Failed to add file or directory watch\n");
        return - 1;
    }

    for (;;) {
        memset(events, 0.sizeof(events));

        // Read the event that occurred
        nbytes = read(fd, events, sizeof(events));
        if (nbytes <= 0) {
            printf("Failed to read events\n");
            continue;
        }

        // Start printing the events that occurred
        for (offset = 0; offset < nbytes; ) {
            event = (struct inotify_event *)&events[offset]; // Get a pointer to the change event

            display_event(argv[1], event);

            offset += sizeof(struct inotify_event) + event->len; // Get the offset of the next change event}}return 0;
}
Copy the code

The above instance logic is relatively simple, and the main steps are as follows:

  • callinotify_initThe function creates ainotifyHandle.
  • Gets the file or directory path to listen from the command line and passesinotify_add_watchThe function adds it toinotifyTo listen.
  • In an infinite loop, throughreadThe function reads the file or directory change event being listened on and callsdisplay_eventFunction prints events.

In the example above, it is difficult to get a pointer to a change event from the events parameter. We can use the following figure to clarify the logic of getting a pointer to a change event:

From the figure above, it is easy to understand how to get a pointer to a changing event from the Events buffer.

Finally, take a look at the effect animation we wrote for our example:

conclusion

This article is about the use of inotify. In the next article, we will cover the principles and implementation of inotify. .