Wake me up before you clo-close!

Git-Repository: Template Solution Solution-Diff (Solution is posted at 18:00 CET)
Workload: 70 lines of code
Important System-Calls: inotify_init(2), inotify_add_watch(2), read(2)

As we already discussed, the ELF's workbench area is really dynamic and a lot of synchronization is going on. But not everything is that entangled and interactive. For some tasks, ELFs just want to wait until some other ELF has put something into the warehouses and become active afterwards. In previous years, ELFs just visited the warehouse from time to time (for example daily) to see if the desired object (e.g., a plank of hard wood) was put into the warehouse. As you imagine, this is time consuming and it also induces latency. If the hard wood arrived shortly after you visited the warehouse, you would only notice on the next day. Wouldn't it be great if the warehouse would inform us directly about the arrival of the hard wood?

inotify - Get informed about changes

As yesterday's task was surely very challenging, we will have a much easier day today. Also, because its my birthday today :-). Today, we will look at the famous inotify(7) interface. With this interface, applications can watch for changes within the file system, so you can write programs that monitor a directory and perform an action if the directory or its contents were changed (by someone).

And actually, this very kernel interface changed my life for the better: In former times, the tail(1) program was poll based and checked its monitored files every second with the -f (for follow) flag. However, this lead to a 1-second delay until lines that my other programs wrote to the log file appeared on the terminal. With inotify, tail -f nowadays gets informed on the file change and immediately can present me with the line on my terminal. This saves me a lot of adrenaline and frustration that I've had with computers. What a gift for my birthday!

OK, so what is special about inotify? Today, I will not talk too much about inotify itself as it is wonderfully described in inotify(7). But I will talk about the usage of file descriptors to get a hold of in-kernel things (we use the word object to sound fancy). When we create a new inotify object in the kernel with inotify_init(2), we get a file descriptor as a return value. And we are supposed to use read(2) to retrieve inotify events. Isn't that wild. The inotify object is surely no file that has a beginning or an end. Nevertheless, it is a stream of events that we want to retrieve. Additionally, the read system call blocks if there is no event available for consumption. Therefore, using the read() operation on this event stream is only weird on the first glance but actually fits if you think about it.

But, there is something weird about these inotify-files: We cannot read byte-wise from this event stream. So a

read(inotify_fd, buffer, /*size = */ 1)

will always return -1. The kernel will only deliver complete events to user-space, which makes the in-kernel implementation much easier as the kernel does not has to track half-read events. Therefore, we also have to use a buffer that is large enough to consume at least one event.


Write a program that watches the current working directory (".") and prints all IN_OPEN | IN_ACCESS | IN_CLOSE events. Print each event as an individual line.

Start your program in your 05-inotify directory and compare its output to the output of the strace cat Makefile call, which traces all system calls of the cat program while it reads the file Makefile from disk. You should be able to correlate the open()/read()/close() system calls to inotify events that you print on the terminal.

My outputs look like this:

$ ./inotify
./Makefile [open]
./Makefile [access]
./Makefile [close_nowrite,close]

And in another terminal, while ./inotify is running and listening to events.

$ strace cat Makefile
openat(AT_FDCWD, "Makefile", O_RDONLY)  = 3
read(3, "inotify: inotify.c\n\tgcc inotify."..., 131072) = 151
close(3)                                = 0