DEV Community

Cover image for I am learning how linux handles file changes
Volker Schukai
Volker Schukai

Posted on

I am learning how linux handles file changes

I think it's always important to get to grips with the basics and not just apply libraries.

How to monitor file changes?

monitor the monitor

What do you do, when you want to familiarize yourself with an area as a software developer? That's right, you look for the corresponding specification.

Each operating system has its own mechanism to react to file changes. Since I work on Unix, I only looked at how Unix works today. Other operating systems like Windows, BSD or macOS have their own mechanism.


Inotify - Monitoring filesystem events

There was dnotify in the beginning, but that's history!

Quoted from Wikipedia:

inotify (inode notify) is a Linux kernel subsystem created by John McCutchan, which monitors changes to the filesystem, and reports those changes to applications. It can be used to automatically update directory views, reload configuration files, log changes, backup, synchronize, and upload.


From the Linux API documentation, we can already learn quite a lot about the mechanism.

Here is just a short quote:

The inotify API provides a mechanism for monitoring filesystem events. Inotify can be used to monitor individual files, or to monitor directories. When a directory is monitored, inotify will return events for the directory itself, and for files inside the directory.

You can also take a closer look at the Linux source code.

Here is the structure of the event:

/*
 * struct inotify_event - structure read from the inotify device for each event
 *
 * When you are watching a directory, you will receive the filename for events
 * such as IN_CREATE, IN_DELETE, IN_OPEN, IN_CLOSE, ..., relative to the wd.
 */
struct inotify_event {
    __s32       wd;     /* watch descriptor */
    __u32       mask;       /* watch mask */
    __u32       cookie;     /* cookie to synchronize two events */
    __u32       len;        /* length (including nulls) of name */
    char        name[]; /* stub for possible name */
};

Enter fullscreen mode Exit fullscreen mode

If we take a closer look at the mode of action and data structures, we get a feeling for how the whole thing works.


Implementation in Go

Let's get back to Go. Of course, Go has good coverage of the Linux API, and we also find functions for Inotify here.

For Windows, of course, there is something comparable.

Under Go, as you would expect, there is an excellent library covering Linux, BSD and Windows.

Thus, one does not have to worry about the individual implementations.

So we put it all together.

First, we need to initialize a new watcher.

As usual in go, we have to take care of possible mistakes. Here, we react to errors with a panic.

w, err := fsnotify.NewWatcher()
if err != nil {
   panic(err)
}

defer w.Close()
Enter fullscreen mode Exit fullscreen mode

Now we add a directory that we want to monitor to the watcher.

err = w.Add("/tmp")
if err != nil {
   panic(err)
}
Enter fullscreen mode Exit fullscreen mode

The real magic then happens in its own go routine.

go func() {
  for {
    select {
      case event, ok := <-w.Events:
        if !ok {
            return
        }

        if event.Has(fsnotify.Write) {
           fmt.Println("modified file:", event.Name)
        }

      case err, ok := <-watcher.Errors:
        if !ok {
            return
        }
        fmt.Println("error:", err)
      }
    }
  }()
Enter fullscreen mode Exit fullscreen mode

With go func() {}() we start our own go routine that runs "parallel" to the main thread.

Go routines are great, I totally love them.

Since we don't have a default statement, the select blocks the execution until an event happens.

If the operating system now reports a change to the /tmp directory, we are informed of this and can react to it.


That's it.

Top comments (0)