This next-gen, dnotify replacement meets file system event-monitoring needs in the 2.6 kernel
Inotify is a file system event-monitoring mechanism slated for inclusion in upcoming Linux™ kernels that is designed to serve as an effective replacement for dnotify, which was the de facto file-monitoring mechanism supported in older kernels. Inotify is a powerful, fine-grained, asynchronous mechanism ideally suited for a variety of file-monitoring needs including, but not limited to, security and performance. Learn how to install inotify and how to build a sample user-space application to respond to file system events.
File system event monitoring is a necessity for many types of programs ranging from file managers to security tools, but dnotify — the standard in earlier kernels — had limitations that left us hoping for something better. With that in mind, meet inotify, a more modern file system event-monitoring alternative.
Why inotify?
There are many reasons to use inotify instead of dnotify. The first is that dnotify requires opening one file descriptor for each directory that you intend to watch for changes. This can become quite costly when you are monitoring several directories at once, since it is possible to reach a per-process file descriptor limit.
Additionally, the file descriptor pins the directory, disallowing the backing device to be unmounted, which causes problems in scenarios involving removable media. When using inotify, if you are watching a file on a file system that is unmounted, the watch is automatically removed and you receive an unmount event.
The second reason dnotify is inferior to inotify is a bit more complex. It is important to note that straightforward file system-monitoring granularity using the dnotify infrastructure exists only at the directory level. For more fine-grained monitoring with dnotify, application programmers are forced to keep a cache of stat structures relating to each directory being monitored. This user space cache of stat structures is needed to determine exactly what change in the directory occurred when a notification signal is received. When notifications are obtained, a list of stat structures is generated and compared with the last known state. Clearly, this technique is sub-optimal.
An additional advantage of inotify is that it uses a file descriptor as the basic interface to let application developers use select and poll to watch the device. This allows for efficient multiplexed I/O and integration with Glib’s mainloop. In contrast, dnotify uses signals with which programmers often have more difficulty and that they find to be less than elegant.
Inotify solves these issues by providing a more elegant API that uses minimal file descriptors and ensures finer granularity of monitoring. Communication with inotify is provided through a device node. For these reasons it should be your clear choice when monitoring files on Linux 2.6 platforms.
Installing inotify
The first step in installing inotify is to determine whether the Linux kernel you are using supports it. The simplest way to check your distribution is to look for the existence of a /dev/inotify device. If this device is present, you may move on to the section Using inotify in a simple application.
At the time this article is being written, inotify is included in Andrew Morton’s Linux 2.6-mm tree, and several Linux distributions are providing inotify-enabled kernels (including Gentoo and Ubuntu) or have supplemental kernel packages with support (for example, Fedora and SuSE). Since Andrew can remove inotify support from his tree if he sees fit and because inotify releases are still frequent at this stage in development, patching from scratch is highly recommended.
If the device is missing, you may need to patch your kernel and create the device.
Patching your kernel for inotify
You can obtain inotify patches from the Linux Kernel Archives (see the Resources section for a link).
You should apply the patch that has the highest version number for your specific kernel. Each distribution handles kernel installation a bit differently, but the following is a generic guideline to follow: Note: Obtain your distribution’s 2.6 Linux kernel source or, if appropriate, the latest stable release from the Linux Kernel Archives.
Begin by going into your kernel source directory:
bash:~$ cd /usr/src
Since you installed the kernel source earlier, you now need to unpack it:
bash:~$ sudo tar jxvf linux-source-2.6.8.1.tar.bz2
Now, make your symlink to the new source tree:
bash:~$ sudo ln -sf linux-source-2.6.8.1 linux
Change your current directory to the kernel source directory you just made:
bash:~$ cd linux
Copy the inotify patch:
bash:~$ sudo cp ~/inotify* /usr/src
Patch the kernel:
bash:~$ sudo patch -p1 < ../inotify*.patch
Build your kernel:
bash:~$ sudo make menuconfig
Configure your kernel as you normally would, making sure to enable the inotify functionality. Add this new kernel to your bootloader as necessary, remembering to maintain your old kernel image and bootloader options. This step varies across bootloaders (see the Resources section for more information about your specific bootloader). Reboot your machine and select your new inotify enabled kernel. Test your new kernel to make sure it functions properly before continuing with this process.
Creating the inotify device
Next, you need to ensure the /dev/inotify device gets created. The following steps will walk you through the process. Important note: The minor number can change, so you must be vigilant about ensuring that it is up to date! If your Linux installation supports udev functionality, it will be kept up to date automatically.
After rebooting into your new kernel, you must obtain the minor number:
bash:~$ dmesg | grep ^inotify
An example of what is returned follows:
inotify device minor=63
Since inotify is a misc device, the major is 10. Create the device node as the root user by executing the following command:
bash:~$ mknod /dev/inotify c 10 63
Note: Replace the "63" with your appropriate minor number as necessary.
Optionally, you should set the permissions as you like. A sample permission set is shown below:
bash:~$ chown root:root /dev/inotify
bash:~$ chmod 666 /dev/inotify
You are now ready to use the inotify device for file system monitoring.
Using inotify in a simple application
To illustrate the use of inotify, I’ll show how to construct a sample program that monitors an arbitrary directory (or simply a single file) for file system events. I’ll begin at a high level to show how easy inotify makes file system monitoring.
Main method
This simple example shows how easy it can be to set up a watch on an arbitrary directory. We’ll look at the major helper routines a bit later on. Get the sample code used in these illustrations in the Download section of this article.
Listing 1. Setting up a watch on a directory
|
Important helper methods
The following are the most important helper routines common to every inotify-based application:
- The opening of the inotify device for reading
- The queuing of events that are read from that device
- The actual per-event handler that allows your application to do something useful with the event notification
I won’t go into the details of queuing events, since several strategies can be used. The sample code provided shows one such method; more advanced multi-threaded approaches can and have been implemented elsewhere. In those implementations, a reader thread simply performs a select() on the inotify device and then copies events to some thread-shared storage (or something like Glib’s asynchronous message queues) where a handler thread acts to process them later.
Listing 2. Opening the inotify device
|
This should look familiar to anyone who has done any programming with files on Linux systems.
Listing 3. The actual event-handling routine
|
Within each case statement you are free to execute any method you have implemented that suits your needs.
With respect to performance monitoring, you can determine which files are read most frequently and the duration they were open. This kind of monitoring is handy, because under some circumstances, if a file is read repeatedly by an application in a small period of time, it may provide a performance enhancement to cache the file in memory rather than going back to the disk.
It’s easy to come up with other examples of event-specific handlers that perform interesting actions. As an example, if you were implementing a metadata storage index for your underlying file system, you could look for file-creation events and trigger a metadata mining operation on that file a short time later. In the context of security, if a file was written in a directory that no one should be writing to, you could trigger some form of system alert. A whole range of interesting opportunities present themselves.
It is important to note that inotify supports a lot of very fine-grained events — for example, CLOSE versus CLOSE_WRITE.
Many of the events listed in the code in this article are probably not something you wish to see each time your code is run. In fact, whenever possible, you can and should request just the subset of events that are useful for your application. The code provided with this article shows many of the events by using the full mask (as performed near line 51 of the main method in our downloadable sample code [see Resources], or line 29 in Listing 1, above) strictly for testing purposes. Application programmers will in general want to be much more selective, and you will need a more specific mask to suit your needs. This will then allow you to remove uninteresting items from the catch statement in the handle_event() method shown earlier.
Conclusion
When applied to such areas as performance monitoring, debugging, and automation, inotify is a powerful, highly granular mechanism for monitoring Linux file systems. Using the code provided in this article, you are ready to start writing applications that can respond to or record file system events in real time with a minimal performance overhead.
Resources
If you found this post useful you may also want to check these out:
