I am learning how linux handles file changes
Tags: go, webdev, programming, linux
Published: 2022-09-03 15:11:26

---
title: I am learning how linux handles file changes
published: true
description: linux uses inotify to communicate changes to the userland.
tags: go,webdev,programming,linux
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wmh6ba4v6hysyrwb4xuq.png
series: A small, self-contained, cross-platform web server
---

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fz68niycrzhl13sod64w.jpg)

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](https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-readdirectorychangesw), 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](https://man7.org/linux/man-pages/man7/inotify.7.html) 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.  ](https://github.com/torvalds/linux/blob/master/include/uapi/linux/inotify.h#L21)

Here is the structure of the event:

```c
/*
 * 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 */
};

```

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](https://pkg.go.dev/golang.org/x/sys/unix#InotifyInit) here.

For [Windows](https://pkg.go.dev/golang.org/x/sys/windows), of course, there is something comparable.

Under Go, as you would expect, there is an [excellent library](https://github.com/fsnotify/fsnotify) 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.

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

defer w.Close()
```

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

```go
err = w.Add("/tmp")
if err != nil {
   panic(err)
}
```

The real magic then happens in its own go routine.

```go
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)
      }
    }
  }()
```

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.**